pax_global_header00006660000000000000000000000064125262027410014513gustar00rootroot0000000000000052 comment=ab27f4c781c3149d1f8e2f980ecf260415a2b943 pexif-0.15/000077500000000000000000000000001252620274100125535ustar00rootroot00000000000000pexif-0.15/PKG-INFO000066400000000000000000000013131252620274100136460ustar00rootroot00000000000000Metadata-Version: 1.1 Name: pexif Version: 0.15 Summary: A module for editing JPEG EXIF data Home-page: http://www.benno.id.au/code/pexif/ Author: Ben Leslie Author-email: benno@benno.id.au License: http://www.opensource.org/licenses/mit-license.php Download-URL: http://www.benno.id.au/code/pexif/pexif-0.15.tar.gz Description: This module allows you to parse and edit the EXIF data tags in a JPEG image. Platform: any Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: License :: OSI Approved :: Python Software Foundation License Classifier: Topic :: Multimedia :: Graphics pexif-0.15/README000066400000000000000000000041721252620274100134370ustar00rootroot00000000000000pexif is a Python library for parsing and more importantly editing EXIF data in JPEG files. This grew out of a need to add GPS tagged data to my images, Unfortunately the other libraries out there couldn't do updates and didn't seem easily architectured to be able to add such a thing. Ain't reusable software grand! Apps: dump_exif.py: Output the EXIF file from a given file. setgps.py: Set the GPS metadata on a file. getgps.py: Get the GPS metadata from a file. noop.py: This is a no-op on a jpeg file. Useful for testing images are preserved across operations using pexif. Note that the binary data will not be exact as pexif will compress unused space in the file, however running it on a file twice should end up with the same data. Examples: hello.py: Add a simple description to a photo. Status: WARNING: This could destroy your images!! Backup your images before using. Currently it parses files from my Canon without a problem, and is able to add a GPS tag without corrupting the rest of the image. References: This work couldn't be done with the reference for the spec. In particular I worked to: http://www.exiv2.org/Exif2-2.PDF For the format of the Canon stuff I used: http://www.burren.cx/david/canon.html For the format of FujiFILM make note: http://www.ozhiker.com/electronics/pjmt/jpeg_info/fujifilm_mn.html Acknowledgments: Nick Carter provided conker.jpg which is used for testing FUJIFILM exif data. Nick Burch provided noexif.jpg which is used for testing inputs that don't have an existing EXIF segment. Nick burch also found an error in GeoTags on the S60. Christopher Jones who inspired updating my examples to show how copying EXIF data from one jpeg to another. Roland Klabunde who also found a bug in the GeoTag functionality. Marcell Lengyel for adding better support for Canon g3 and FujiFilm z5fd. Andrew Baumann for finding a bug in dealing with extended IFD sections, and supplying the timezone adjustment script. pexif-0.15/pexif.py000066400000000000000000001272611252620274100142510ustar00rootroot00000000000000""" pexif is a module which allows you to view and modify meta-data in JPEG/JFIF/EXIF files. The main way to use this is to create an instance of the JpegFile class. This should be done using one of the static factory methods fromFile, fromString or fromFd. After manipulating the object you can then write it out using one of the writeFile, writeString or writeFd methods. The get_exif() method on JpegFile returns the ExifSegment if one exists. Example: jpeg = pexif.JpegFile.fromFile("foo.jpg") exif = jpeg.get_exif() .... jpeg.writeFile("new.jpg") For photos that don't currently have an exef segment you can specify an argument which will create the exef segment if it doesn't exist. Example: jpeg = pexif.JpegFile.fromFile("foo.jpg") exif = jpeg.get_exif(create=True) .... jpeg.writeFile("new.jpg") The JpegFile class handles file that are formatted in something approach the JPEG specification (ISO/IEC 10918-1) Annex B 'Compressed Data Formats', and JFIF and EXIF standard. In particular, the way a 'jpeg' file is treated by pexif is that a JPEG file is made of a series of segments followed by the image data. In particular it should look something like: [ SOI | | SOS | image data | EOI ] So, the library expects a Start-of-Image marker, followed by an arbitrary number of segment (assuming that a segment has the format: [ <0xFF> ] and that there are no gaps between segments. The last segment must be the Start-of-Scan header, and the library assumes that following Start-of-Scan comes the image data, finally followed by the End-of-Image marker. This is probably not sufficient to handle arbitrary files conforming to the JPEG specs, but it should handle files that conform to JFIF or EXIF, as well as files that conform to neither but have both JFIF and EXIF application segment (which is the majority of files in existence!). When writing out files all segment will be written out in the order in which they were read. Any 'unknown' segment will be written out as is. Note: This may or may not corrupt the data. If the segment format relies on absolute references then this library may still corrupt that segment! Can have a JpegFile in two modes: Read Only and Read Write. Read Only mode: trying to access missing elements will result in an AttributeError. Read Write mode: trying to access missing elements will automatically create them. E.g: img.exif.primary. .geo .interop .exif. .exif.makernote. .thumbnail img.flashpix.<...> img.jfif. img.xmp E.g: try: print img.exif.tiff.exif.FocalLength except AttributeError: print "No Focal Length data" """ import StringIO import sys from struct import unpack, pack MAX_HEADER_SIZE = 64 * 1024 DELIM = 0xff EOI = 0xd9 SOI_MARKER = chr(DELIM) + '\xd8' EOI_MARKER = chr(DELIM) + '\xd9' TIFF_OFFSET = 6 TIFF_TAG = 0x2a DEBUG = 0 # By default, if we find a makernote with an unknown format, we # simply skip over it. In some cases, it makes sense to raise a # real error. # # Set to `unknown_make_note_as_error` to True, if errors should # be raised. unknown_maker_note_as_error = False def debug(*debug_string): """Used for print style debugging. Enable by setting the global DEBUG to 1.""" if DEBUG: for each in debug_string: print each, print class DefaultSegment: """DefaultSegment represents a particluar segment of a JPEG file. This class is instantiated by JpegFile when parsing Jpeg files and is not intended to be used directly by the programmer. This base class is used as a default which doesn't know about the internal structure of the segment. Other classes subclass this to provide extra information about a particular segment. """ def __init__(self, marker, fd, data, mode): """The constructor for DefaultSegment takes the marker which identifies the segments, a file object which is currently positioned at the end of the segment. This allows any subclasses to potentially extract extra data from the stream. Data contains the contents of the segment.""" self.marker = marker self.data = data self.mode = mode self.fd = fd assert mode in ["rw", "ro"] if self.data is not None: self.parse_data(data) class InvalidSegment(Exception): """This exception may be raised by sub-classes in cases when they can't correctly identify the segment.""" pass def write(self, fd): """This method is called by JpegFile when writing out the file. It must write out any data in the segment. This shouldn't in general be overloaded by subclasses, they should instead override the get_data() method.""" fd.write('\xff') fd.write(pack('B', self.marker)) data = self.get_data() fd.write(pack('>H', len(data) + 2)) fd.write(data) def get_data(self): """This method is called by write to generate the data for this segment. It should be overloaded by subclasses.""" return self.data def parse_data(self, data): """This method is called be init to parse any data for the segment. It should be overloaded by subclasses rather than overloading __init__""" pass def dump(self, fd): """This is called by JpegFile.dump() to output a human readable representation of the segment. Subclasses should overload this to provide extra information.""" print >> fd, " Section: [%5s] Size: %6d" % \ (jpeg_markers[self.marker][0], len(self.data)) class StartOfScanSegment(DefaultSegment): """The StartOfScan segment needs to be treated specially as the actual image data directly follows this segment, and that data is not included in the size as reported in the segment header. This instances of this class are created by JpegFile and it should not be subclassed. """ def __init__(self, marker, fd, data, mode): DefaultSegment.__init__(self, marker, fd, data, mode) # For SOS we also pull out the actual data img_data = fd.read() # Usually the EOI marker will be at the end of the file, # optimise for this case if img_data[-2:] == EOI_MARKER: remaining = 2 else: # We need to search for i in range(len(img_data) - 2): if img_data[i:i + 2] == EOI_MARKER: break else: raise JpegFile.InvalidFile("Unable to find EOI marker.") remaining = len(img_data) - i self.img_data = img_data[:-remaining] fd.seek(-remaining, 1) def write(self, fd): """Write segment data to a given file object""" DefaultSegment.write(self, fd) fd.write(self.img_data) def dump(self, fd): """Dump as ascii readable data to a given file object""" print >> fd, " Section: [ SOS] Size: %6d Image data size: %6d" % \ (len(self.data), len(self.img_data)) class ExifType: """The ExifType class encapsulates the data types used in the Exif spec. These should really be called TIFF types probably. This could be replaced by named tuples in python 2.6.""" lookup = {} def __init__(self, type_id, name, size): """Create an ExifType with a given name, size and type_id""" self.id = type_id self.name = name self.size = size ExifType.lookup[type_id] = self BYTE = ExifType(1, "byte", 1).id ASCII = ExifType(2, "ascii", 1).id SHORT = ExifType(3, "short", 2).id LONG = ExifType(4, "long", 4).id RATIONAL = ExifType(5, "rational", 8).id UNDEFINED = ExifType(7, "undefined", 1).id SLONG = ExifType(9, "slong", 4).id SRATIONAL = ExifType(10, "srational", 8).id def exif_type_size(exif_type): """Return the size of a type""" return ExifType.lookup.get(exif_type).size class Rational: """A simple fraction class. Python 2.6 could use the inbuilt Fraction class.""" def __init__(self, num, den): """Create a number fraction num/den.""" self.num = num self.den = den def __repr__(self): """Return a string representation of the fraction.""" return "%s / %s" % (self.num, self.den) def as_tuple(self): """Return the fraction a numerator, denominator tuple.""" return (self.num, self.den) class IfdData(object): """Base class for IFD""" name = "Generic Ifd" tags = {} embedded_tags = {} def special_handler(self, tag, data): """special_handler method can be over-ridden by subclasses to specially handle the conversion of tags from raw format into Python data types.""" pass def ifd_handler(self, data): """ifd_handler method can be over-ridden by subclasses to specially handle conversion of the Ifd as a whole into a suitable python representation.""" pass def extra_ifd_data(self, offset): """extra_ifd_data method can be over-ridden by subclasses to specially handle conversion of the Python Ifd representation back into a byte stream.""" return "" def has_key(self, key): return self[key] is not None def __setattr__(self, name, value): for key, entry in self.tags.items(): if entry[1] == name: self[key] = value return for key, entry in self.embedded_tags.items(): if entry[0] == name: if not isinstance(value, entry[1]): raise TypeError("Values assigned to '{}' must be instances of {}".format(entry[0], entry[1])) self[key] = value return raise AttributeError("Invalid attribute '{}'".format(name)) def __delattr__(self, name): for key, entry in self.tags.items(): if entry[1] == name: del self[key] break else: raise AttributeError("Invalid attribute '{}'".format(name)) def __getattr__(self, name): for key, entry in self.tags.items(): if entry[1] == name: x = self[key] if x is None: raise AttributeError return x for key, entry in self.embedded_tags.items(): if entry[0] == name: if self.has_key(key): return self[key] else: if self.mode == "rw": new = entry[1](self.e, 0, "rw", self.exif_file) self[key] = new return new else: raise AttributeError raise AttributeError("%s not found.. %s" % (name, self.embedded_tags)) def __getitem__(self, key): if type(key) == type(""): try: return self.__getattr__(key) except AttributeError: return None for entry in self.entries: if key == entry[0]: if entry[1] == ASCII and not entry[2] is None: return entry[2].strip('\0') else: return entry[2] return None def __delitem__(self, key): if type(key) == type(""): try: return self.__delattr__(key) except AttributeError: return None for entry in self.entries: if key == entry[0]: self.entries.remove(entry) def __setitem__(self, key, value): if type(key) == type(""): return self.__setattr__(key, value) found = 0 if len(self.tags[key]) < 3: raise "Error: Tags aren't set up correctly, should have tag type." if self.tags[key][2] == ASCII: if value is not None and not value.endswith('\0'): value = value + '\0' for i in range(len(self.entries)): if key == self.entries[i][0]: found = 1 entry = list(self.entries[i]) if value is None: del self.entries[i] else: entry[2] = value self.entries[i] = tuple(entry) break if not found: # Find type... # Not quite enough yet... self.entries.append((key, self.tags[key][2], value)) return def __init__(self, e, offset, exif_file, mode, data=None): object.__setattr__(self, 'exif_file', exif_file) object.__setattr__(self, 'mode', mode) object.__setattr__(self, 'e', e) object.__setattr__(self, 'entries', []) if data is None: return num_entries = unpack(e + 'H', data[offset:offset+2])[0] next = unpack(e + "I", data[offset+2+12*num_entries: offset+2+12*num_entries+4])[0] debug("OFFSET %s - %s" % (offset, next)) for i in range(num_entries): start = (i * 12) + 2 + offset debug("START: ", start) entry = unpack(e + "HHII", data[start:start+12]) tag, exif_type, components, the_data = entry debug("%s %s %s %s %s" % (hex(tag), exif_type, exif_type_size(exif_type), components, the_data)) byte_size = exif_type_size(exif_type) * components if tag in self.embedded_tags: try: actual_data = self.embedded_tags[tag][1](e, the_data, exif_file, self.mode, data) except JpegFile.SkipTag as exc: # If the tag couldn't be parsed, and raised 'SkipTag' # then we just continue. continue else: if byte_size > 4: debug(" ...offset %s" % the_data) the_data = data[the_data:the_data+byte_size] else: the_data = data[start+8:start+8+byte_size] if exif_type == BYTE or exif_type == UNDEFINED: actual_data = list(the_data) elif exif_type == ASCII: if the_data[-1] != '\0': actual_data = the_data + '\0' # raise JpegFile.InvalidFile("ASCII tag '%s' not # NULL-terminated: %s [%s]" % (self.tags.get(tag, # (hex(tag), 0))[0], the_data, map(ord, the_data))) # print "ASCII tag '%s' not NULL-terminated: # %s [%s]" % (self.tags.get(tag, (hex(tag), 0))[0], # the_data, map(ord, the_data)) actual_data = the_data elif exif_type == SHORT: actual_data = list(unpack(e + ("H" * components), the_data)) elif exif_type == LONG: actual_data = list(unpack(e + ("I" * components), the_data)) elif exif_type == SLONG: actual_data = list(unpack(e + ("i" * components), the_data)) elif exif_type == RATIONAL or exif_type == SRATIONAL: t = 'II' if exif_type == RATIONAL else 'ii' actual_data = [] for i in range(components): actual_data.append(Rational(*unpack(e + t, the_data[i*8: i*8+8]))) else: raise "Can't handle this" if (byte_size > 4): debug("%s" % actual_data) self.special_handler(tag, actual_data) entry = (tag, exif_type, actual_data) self.entries.append(entry) debug("%-40s %-10s %6d %s" % (self.tags.get(tag, (hex(tag), 0))[0], ExifType.lookup[exif_type], components, actual_data)) self.ifd_handler(data) def isifd(self, other): """Return true if other is an IFD""" return issubclass(other.__class__, IfdData) def getdata(self, e, offset, last=0): data_offset = offset+2+len(self.entries)*12+4 output_data = "" out_entries = [] # Add any specifc data for the particular type extra_data = self.extra_ifd_data(data_offset) data_offset += len(extra_data) output_data += extra_data for tag, exif_type, the_data in self.entries: magic_type = exif_type if (self.isifd(the_data)): debug("-> Magic..") sub_data, next_offset = the_data.getdata(e, data_offset, 1) the_data = [data_offset] debug("<- Magic", next_offset, data_offset, len(sub_data), data_offset + len(sub_data)) data_offset += len(sub_data) assert(next_offset == data_offset) output_data += sub_data magic_type = exif_type if exif_type != 4: magic_components = len(sub_data) else: magic_components = 1 exif_type = 4 # LONG byte_size = 4 components = 1 else: magic_components = components = len(the_data) byte_size = exif_type_size(exif_type) * components if exif_type == BYTE or exif_type == UNDEFINED: actual_data = "".join(the_data) elif exif_type == ASCII: actual_data = the_data elif exif_type == SHORT: actual_data = pack(e + ("H" * components), *the_data) elif exif_type == LONG: actual_data = pack(e + ("I" * components), *the_data) elif exif_type == SLONG: actual_data = pack(e + ("i" * components), *the_data) elif exif_type == RATIONAL or exif_type == SRATIONAL: t = 'II' if exif_type == RATIONAL else 'ii' actual_data = "" for i in range(components): actual_data += pack(e + t, *the_data[i].as_tuple()) else: raise "Can't handle this", exif_type if (byte_size) > 4: output_data += actual_data actual_data = pack(e + "I", data_offset) data_offset += byte_size else: actual_data = actual_data + '\0' * (4 - len(actual_data)) out_entries.append((tag, magic_type, magic_components, actual_data)) data = pack(e + 'H', len(self.entries)) for entry in out_entries: data += pack(self.e + "HHI", *entry[:3]) data += entry[3] next_offset = data_offset if last: data += pack(self.e + "I", 0) else: data += pack(self.e + "I", next_offset) data += output_data assert (next_offset == offset+len(data)) return data, next_offset def dump(self, f, indent=""): """Dump the IFD file""" print >> f, indent + "<--- %s start --->" % self.name for entry in self.entries: tag, exif_type, data = entry if exif_type == ASCII: data = data.strip('\0') if (self.isifd(data)): data.dump(f, indent + " ") else: if data and len(data) == 1: data = data[0] print >> f, indent + " %-40s %s" % \ (self.tags.get(tag, (hex(tag), 0))[0], data) print >> f, indent + "<--- %s end --->" % self.name class IfdInterop(IfdData): name = "Interop" tags = { # Interop stuff 0x0001: ("Interoperability index", "InteroperabilityIndex"), 0x0002: ("Interoperability version", "InteroperabilityVersion"), 0x1000: ("Related image file format", "RelatedImageFileFormat"), 0x1001: ("Related image file width", "RelatedImageFileWidth"), 0x1002: ("Related image file length", "RelatedImageFileLength"), } class CanonIFD(IfdData): tags = { 0x0006: ("Image Type", "ImageType"), 0x0007: ("Firmware Revision", "FirmwareRevision"), 0x0008: ("Image Number", "ImageNumber"), 0x0009: ("Owner Name", "OwnerName"), 0x000c: ("Camera serial number", "SerialNumber"), 0x000f: ("Customer functions", "CustomerFunctions") } name = "Canon" class FujiIFD(IfdData): tags = { 0x0000: ("Note version", "NoteVersion"), 0x1000: ("Quality", "Quality"), 0x1001: ("Sharpness", "Sharpness"), 0x1002: ("White balance", "WhiteBalance"), 0x1003: ("Color", "Color"), 0x1004: ("Tone", "Tone"), 0x1010: ("Flash mode", "FlashMode"), 0x1011: ("Flash strength", "FlashStrength"), 0x1020: ("Macro", "Macro"), 0x1021: ("Focus mode", "FocusMode"), 0x1030: ("Slow sync", "SlowSync"), 0x1031: ("Picture mode", "PictureMode"), 0x1100: ("Motor or bracket", "MotorOrBracket"), 0x1101: ("Sequence number", "SequenceNumber"), 0x1210: ("FinePix Color", "FinePixColor"), 0x1300: ("Blur warning", "BlurWarning"), 0x1301: ("Focus warning", "FocusWarning"), 0x1302: ("AE warning", "AEWarning") } name = "FujiFilm" def getdata(self, e, offset, last=0): pre_data = "FUJIFILM" pre_data += pack(". Got <%s>." % header) # The it has its own offset ifd_offset = unpack(", " "expecting " % exif) tiff_data = data[TIFF_OFFSET:] data = None # Don't need or want data for now on. self.tiff_endian = tiff_data[:2] if self.tiff_endian == "II": self.e = "<" elif self.tiff_endian == "MM": self.e = ">" else: raise JpegFile.InvalidFile("Bad TIFF endian header. Got <%s>, " "expecting or " % self.tiff_endian) tiff_tag, tiff_offset = unpack(self.e + 'HI', tiff_data[2:8]) if (tiff_tag != TIFF_TAG): raise JpegFile.InvalidFile("Bad TIFF tag. Got <%x>, expecting " "<%x>" % (tiff_tag, TIFF_TAG)) # Ok, the header parse out OK. Now we parse the IFDs contained in # the APP1 header. # We use this loop, even though we can really only expect and support # two IFDs, the Attribute data and the Thumbnail data offset = tiff_offset count = 0 while offset: count += 1 num_entries = unpack(self.e + 'H', tiff_data[offset:offset+2])[0] start = 2 + offset + (num_entries*12) if (count == 1): ifd = IfdTIFF(self.e, offset, self, self.mode, tiff_data) elif (count == 2): ifd = IfdThumbnail(self.e, offset, self, self.mode, tiff_data) else: raise JpegFile.InvalidFile() self.ifds.append(ifd) # Get next offset offset = unpack(self.e + "I", tiff_data[start:start+4])[0] def dump(self, fd): print >> fd, " Section: [ EXIF] Size: %6d" % (len(self.data)) for ifd in self.ifds: ifd.dump(fd) def get_data(self): ifds_data = "" next_offset = 8 for ifd in self.ifds: debug("OUT IFD") new_data, next_offset = ifd.getdata(self.e, next_offset, ifd == self.ifds[-1]) ifds_data += new_data data = "" data += "Exif\0\0" data += self.tiff_endian data += pack(self.e + "HI", 42, 8) data += ifds_data return data def get_primary(self, create=False): """Return the attributes image file descriptor. If it doesn't exit return None, unless create is True in which case a new descriptor is created.""" if len(self.ifds) > 0: return self.ifds[0] else: if create: assert self.mode == "rw" new_ifd = IfdTIFF(self.e, None, self, "rw") self.ifds.insert(0, new_ifd) return new_ifd else: return None def _get_property(self): if self.mode == "rw": return self.get_primary(True) else: primary = self.get_primary() if primary is None: raise AttributeError return primary primary = property(_get_property) jpeg_markers = { 0xc0: ("SOF0", []), 0xc2: ("SOF2", []), 0xc4: ("DHT", []), 0xda: ("SOS", [StartOfScanSegment]), 0xdb: ("DQT", []), 0xdd: ("DRI", []), 0xe0: ("APP0", []), 0xe1: ("APP1", [ExifSegment]), 0xe2: ("APP2", []), 0xe3: ("APP3", []), 0xe4: ("APP4", []), 0xe5: ("APP5", []), 0xe6: ("APP6", []), 0xe7: ("APP7", []), 0xe8: ("APP8", []), 0xe9: ("APP9", []), 0xea: ("APP10", []), 0xeb: ("APP11", []), 0xec: ("APP12", []), 0xed: ("APP13", []), 0xee: ("APP14", []), 0xef: ("APP15", []), 0xfe: ("COM", []), } APP1 = 0xe1 class JpegFile: """JpegFile object. You should create this using one of the static methods fromFile, fromString or fromFd. The JpegFile object allows you to examine and modify the contents of the file. To write out the data use one of the methods writeFile, writeString or writeFd. To get an ASCII dump of the data in a file use the dump method.""" def fromFile(filename, mode="rw"): """Return a new JpegFile object from a given filename.""" with open(filename, "rb") as f: return JpegFile(f, filename=filename, mode=mode) fromFile = staticmethod(fromFile) def fromString(str, mode="rw"): """Return a new JpegFile object taking data from a string.""" return JpegFile(StringIO.StringIO(str), "from buffer", mode=mode) fromString = staticmethod(fromString) def fromFd(fd, mode="rw"): """Return a new JpegFile object taking data from a file object.""" return JpegFile(fd, "fd <%d>" % fd.fileno(), mode=mode) fromFd = staticmethod(fromFd) class SkipTag(Exception): """This exception is raised if a give tag should be skipped.""" pass class InvalidFile(Exception): """This exception is raised if a given file is not able to be parsed.""" pass class NoSection(Exception): """This exception is raised if a section is unable to be found.""" pass def __init__(self, input, filename=None, mode="rw"): """JpegFile Constructor. input is a file object, and filename is a string used to name the file. (filename is used only for display functions). You shouldn't use this function directly, but rather call one of the static methods fromFile, fromString or fromFd.""" self.filename = filename self.mode = mode # input is the file descriptor soi_marker = input.read(len(SOI_MARKER)) # The very first thing should be a start of image marker if (soi_marker != SOI_MARKER): raise self.InvalidFile("Error reading soi_marker. Got <%s> " "should be <%s>" % (soi_marker, SOI_MARKER)) # Now go through and find all the blocks of data segments = [] while 1: head = input.read(2) delim, mark = unpack(">BB", head) if (delim != DELIM): raise self.InvalidFile("Error, expecting delimiter. " "Got <%s> should be <%s>" % (delim, DELIM)) if mark == EOI: # Hit end of image marker, game-over! break head2 = input.read(2) size = unpack(">H", head2)[0] data = input.read(size-2) possible_segment_classes = jpeg_markers[mark][1] + [DefaultSegment] # Try and find a valid segment class to handle # this data for segment_class in possible_segment_classes: try: # Note: Segment class may modify the input file # descriptor. This is expected. attempt = segment_class(mark, input, data, self.mode) segments.append(attempt) break except DefaultSegment.InvalidSegment: # It wasn't this one so we try the next type. # DefaultSegment will always work. continue self._segments = segments def writeString(self): """Write the JpegFile out to a string. Returns a string.""" f = StringIO.StringIO() self.writeFd(f) return f.getvalue() def writeFile(self, filename): """Write the JpegFile out to a file named filename.""" output = open(filename, "wb") self.writeFd(output) def writeFd(self, output): """Write the JpegFile out on the file object output.""" output.write(SOI_MARKER) for segment in self._segments: segment.write(output) output.write(EOI_MARKER) def dump(self, f=sys.stdout): """Write out ASCII representation of the file on a given file object. Output default to stdout.""" print >> f, "" % self.filename for segment in self._segments: segment.dump(f) def get_exif(self, create=False): """get_exif returns a ExifSegment if one exists for this file. If the file does not have an exif segment and the create is false, then return None. If create is true, a new exif segment is added to the file and returned.""" for segment in self._segments: if segment.__class__ == ExifSegment: return segment if create: return self.add_exif() else: return None def add_exif(self): """add_exif adds a new ExifSegment to a file, and returns it. When adding an EXIF segment is will add it at the start of the list of segments.""" assert self.mode == "rw" new_segment = ExifSegment(APP1, None, None, "rw") self._segments.insert(0, new_segment) return new_segment def _get_exif(self): """Exif Attribute property""" if self.mode == "rw": return self.get_exif(True) else: exif = self.get_exif(False) if exif is None: raise AttributeError return exif exif = property(_get_exif) def get_geo(self): """Return a tuple of (latitude, longitude).""" def convert(x): (deg, min, sec) = x return (float(deg.num) / deg.den) + \ (1/60.0 * float(min.num) / min.den) + \ (1/3600.0 * float(sec.num) / sec.den) if not hasattr(self.exif.primary, 'GPSIFD'): raise self.NoSection, "File %s doesn't have a GPS section." % \ self.filename gps = self.exif.primary.GPS lat = convert(gps.GPSLatitude) lng = convert(gps.GPSLongitude) if gps.GPSLatitudeRef == "S": lat = -lat if gps.GPSLongitudeRef == "W": lng = -lng return lat, lng SEC_DEN = 50000000 def _parse(val): sign = 1 if val < 0: val = -val sign = -1 deg = int(val) other = (val - deg) * 60 minutes = int(other) secs = (other - minutes) * 60 secs = long(secs * JpegFile.SEC_DEN) return (sign, deg, minutes, secs) _parse = staticmethod(_parse) def set_geo(self, lat, lng): """Set the GeoLocation to a given lat and lng""" if self.mode != "rw": raise RWError gps = self.exif.primary.GPS sign, deg, min, sec = JpegFile._parse(lat) ref = "N" if sign < 0: ref = "S" gps.GPSLatitudeRef = ref gps.GPSLatitude = [Rational(deg, 1), Rational(min, 1), Rational(sec, JpegFile.SEC_DEN)] sign, deg, min, sec = JpegFile._parse(lng) ref = "E" if sign < 0: ref = "W" gps.GPSLongitudeRef = ref gps.GPSLongitude = [Rational(deg, 1), Rational(min, 1), Rational(sec, JpegFile.SEC_DEN)] pexif-0.15/scripts/000077500000000000000000000000001252620274100142425ustar00rootroot00000000000000pexif-0.15/scripts/dump_exif.py000077500000000000000000000007411252620274100166010ustar00rootroot00000000000000#!/usr/bin/env python from pexif import JpegFile import sys usage = """Usage: dump_exif.py filename.jpg""" if len(sys.argv) != 2: print >> sys.stderr, usage sys.exit(1) try: ef = JpegFile.fromFile(sys.argv[1]) ef.dump() except IOError: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error opening file:", value except JpegFile.InvalidFile: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error opening file:", value pexif-0.15/scripts/getgps.py000077500000000000000000000011441252620274100161100ustar00rootroot00000000000000#!/usr/bin/env python from pexif import JpegFile import sys usage = """Usage: getgps.py filename.jpg""" if len(sys.argv) != 2: print >> sys.stderr, usage sys.exit(1) try: ef = JpegFile.fromFile(sys.argv[1]) print ef.get_geo() except IOError: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error opening file:", value except JpegFile.NoSection: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error get GPS info:", value except JpegFile.InvalidFile: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error opening file:", value pexif-0.15/scripts/noop.py000077500000000000000000000012401252620274100155670ustar00rootroot00000000000000#!/usr/bin/env python from pexif import JpegFile import sys usage = """Usage: dump_exif.py filename.jpg out.jpg""" if len(sys.argv) != 3: print >> sys.stderr, usage sys.exit(1) try: ef = JpegFile.fromFile(sys.argv[1]) except IOError: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error opening file:", value sys.exit(1) except JpegFile.InvalidFile: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error opening file:", value sys.exit(1) try: ef.writeFile(sys.argv[2]) except IOError: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error saving file:", value sys.exit(1) pexif-0.15/scripts/setgps.py000077500000000000000000000012441252620274100161250ustar00rootroot00000000000000#!/usr/bin/env python from pexif import JpegFile import sys usage = """Usage: setgps.py filename.jpg lat lng""" if len(sys.argv) != 4: print >> sys.stderr, usage sys.exit(1) try: ef = JpegFile.fromFile(sys.argv[1]) ef.set_geo(float(sys.argv[2]), float(sys.argv[3])) except IOError: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error opening file:", value except JpegFile.InvalidFile: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error opening file:", value try: ef.writeFile(sys.argv[1]) except IOError: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error saving file:", value pexif-0.15/scripts/timezone.py000066400000000000000000000042271252620274100164530ustar00rootroot00000000000000#!/usr/bin/env python """ Utility to adjust the EXIF timestamps in JPEG files by a constant offset. Requires Benno's pexif library: http://code.google.com/p/pexif/ -- Andrew Baumann , 20080716 """ import sys from pexif import JpegFile, EXIF_OFFSET from datetime import timedelta, datetime from optparse import OptionParser DATETIME_EMBEDDED_TAGS = ["DateTimeOriginal", "DateTimeDigitized"] TIME_FORMAT = '%Y:%m:%d %H:%M:%S' def parse_args(): p = OptionParser(usage='%prog hours file.jpg...', description='adjusts timestamps in EXIF metadata by given offset') options, args = p.parse_args() if len(args) < 2: p.error('not enough arguments') try: hours = int(args[0]) except: p.error('invalid time offset, must be an integral number of hours') return hours, args[1:] def adjust_time(primary, delta): def adjust_tag(timetag, delta): dt = datetime.strptime(timetag, TIME_FORMAT) dt += delta return dt.strftime(TIME_FORMAT) if primary.DateTime: primary.DateTime = adjust_tag(primary.DateTime, delta) embedded = primary[EXIF_OFFSET] if embedded: for tag in DATETIME_EMBEDDED_TAGS: if embedded[tag]: embedded[tag] = adjust_tag(embedded[tag], delta) def main(): hours, files = parse_args() delta = timedelta(hours=hours) for fname in files: try: jf = JpegFile.fromFile(fname) except (IOError, JpegFile.InvalidFile): type, value, traceback = sys.exc_info() print >> sys.stderr, "Error reading %s:" % fname, value return 1 exif = jf.get_exif() if exif: primary = exif.get_primary() if exif is None or primary is None: print >> sys.stderr, "%s has no EXIF tag, skipping" % fname continue adjust_time(primary, delta) try: jf.writeFile(fname) except IOError: type, value, traceback = sys.exc_info() print >> sys.stderr, "Error saving %s:" % fname, value return 1 return 0 if __name__ == "__main__": sys.exit(main()) pexif-0.15/setup.py000066400000000000000000000021561252620274100142710ustar00rootroot00000000000000#!/usr/bin/env python from distutils.core import setup version = "0.15" """Setup script for pexif""" setup ( name = "pexif", version = version, description = "A module for editing JPEG EXIF data", long_description = "This module allows you to parse and edit the EXIF data tags in a JPEG image.", author = "Ben Leslie", author_email = "benno@benno.id.au", url = "http://www.benno.id.au/code/pexif/", download_url = "http://www.benno.id.au/code/pexif/pexif-%s.tar.gz" % version, license = "http://www.opensource.org/licenses/mit-license.php", py_modules = ["pexif"], scripts = ["scripts/dump_exif.py", "scripts/setgps.py", "scripts/getgps.py", "scripts/noop.py", "scripts/timezone.py"], platforms = ["any"], classifiers = ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python", "License :: OSI Approved :: Python Software Foundation License", "Topic :: Multimedia :: Graphics"] ) pexif-0.15/test/000077500000000000000000000000001252620274100135325ustar00rootroot00000000000000pexif-0.15/test/data/000077500000000000000000000000001252620274100144435ustar00rootroot00000000000000pexif-0.15/test/data/conker.jpg000066400000000000000000000121021252620274100164220ustar00rootroot00000000000000JFIFHH ExifII*  (1%2; iFUJIFILMFinePix S7000 HHDigital Camera FinePix S7000 Ver1.002004:09:26 16:40:26 Nick Carter%"'0220   " *2  :|B0100  `h  p d2004:09:26 16:40:262004:09:26 16:40:26 4dddd,ddFUJIFILM 0130  !"#012C{=O gm+.c#ASCIIConkerP<:JFIFHH AppleMark   !'")(&"&%+0=4+-:.%&5I6:?AEEE*3KQKCP=CEB   B,&,BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz<P" ?Atr[ya#67|* ڳ=РeIsV`8 ˨a!z)c K.[S2"\@ Q\renV!BV[ʐ's9>*n#O0xֽgZFJȡs":vSY*_Bi^[zm!8VQr:=m^y,v59f2il@0KR~C{=O gm+.c# g OX:q8m\$2I fjhZ`y=3/Ud!i7֒ ?pzeϗ$s[ܩJ`k /]EY=sִ5E%.Hݸ݌w+ͫM?j;ԡ'ԯ Ϲej[E/_=1m&ʱI"sd\NX}XT5aZwwfmφa=  &R̫sz6:Wn {W,r\N ܠzOlFugXX<]=Iz o4MM'l{!Z׬R$I  f۔\n,O5_1B_ Cwgm}c:@J35&>#A4{f'kv+[٧/=OUz#fdf"U5Y`e%Ox"u[LUdV%cP\;W7Ҥo2H烃֚#$fOJJ(^]KWZ䄬r c%g *2t=u3roqFzU[LA'0J< vkŒs8FSRoC   ")"1+320+/.6俷#!1AQaq?POu[O ᢽb Z-RAn/q1m %cP})0F\5UIKbmHB[:\ ~@VGWQ@+38uH+ [a{ `;IIbLڝ$"yBUT|Ty,{UP( 2,4^*Jx4SQm"E2EQ`p4$E"J %p>ح1*R*8\Д;ӜT .=?pY2\Җ,fuWEX -T"t iigg1lE#s puSmn߸b`/!8%'C E\߈JxNz1֯dDPA,Dkw~l*P&{whJ~SUOa/[ !H' 9 w s0q`]_ȂOpexif-0.15/test/data/conker.txt000066400000000000000000000110231252620274100164620ustar00rootroot00000000000000 Section: [ APP0] Size: 14 Section: [ EXIF] Size: 3341 <--- TIFF Ifd start ---> Camera Make FUJIFILM Camera Model FinePix S7000 Orientation of image 1 X Resolution 72 / 1 Y Resolution 72 / 1 Unit of X and Y resolution 2 Camera Software Digital Camera FinePix S7000 Ver1.00 File change data and time 2004:09:26 16:40:26 Y and C positioning 2 Copyright holder Artist Nick Carter <--- Extended EXIF start ---> Exposure Time 10 / 3000 F Number 500 / 100 Exposure Program 2 ISO Speed Rating 160 Exif Version ['0', '2', '2', '0'] Date of original data generation 2004:09:26 16:40:26 Date of digital data generation 2004:09:26 16:40:26 Meaning of each component ['\x01', '\x02', '\x03', '\x00'] Image compression mode 16 / 10 Shutter speed 820 / 100 Aperture 460 / 100 Brightness 717 / 100 Exposure bias 0 / 100 Maximum lens apeture 300 / 100 Metering mode 5 Light mode 0 Flash 16 Lens focal length 1800 / 100 <--- FujiFilm start ---> Note version ['0', '1', '3', '0'] Quality C{=O Sharpness 3 White balance 0 Color 0 Flash mode 2 Flash strength -1205113497 / 593702593 Macro 1 Focus mode 0 0x1022 1 0x1023 [2024, 1520] Slow sync 0 Picture mode 0 0x1032 1 Motor or bracket 0 Sequence number 0 0x1200 0 FinePix Color 0 Blur warning 0 Focus warning 0 AE warning 0 <--- FujiFilm end ---> Supported Flashpix version ['0', '1', '0', '0'] Color Space Information 1 Valid image width 4048 Valid image height 3040 0xa005 3258 Focal plane X resolution 5263 / 1 Focal plane Y resolution 5263 / 1 Focal plane resolution unit 3 Sensing method 2 File source  Scene type  Customer image processing 0 Exposure mode 0 White balance 0 Scene capture type 0 Sharpness 0 Subject distance range 1 User comments ['A', 'S', 'C', 'I', 'I', '\x00', '\x00', '\x00', 'C', 'o', 'n', 'k', 'e', 'r', '\x00'] <--- Extended EXIF end ---> <--- TIFF Ifd end ---> <--- Thumbnail start ---> Image width 80 Image height 60 Compression Scheme 6 Orientation of image 1 Offset to JPEG SOI 1229 Bytes of JPEG data 2106 <--- Thumbnail end ---> Section: [ DQT] Size: 65 Section: [ DQT] Size: 65 Section: [ SOF2] Size: 15 Section: [ DHT] Size: 23 Section: [ DHT] Size: 22 Section: [ SOS] Size: 10 Image data size: 1595 pexif-0.15/test/data/noexif.jpg000066400000000000000000004112051252620274100164400ustar00rootroot00000000000000JFIF    $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222 }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?hE4eQ\H7BvA [bI4*X:,f_vɮu\שxKɌLX"v8#oCIVm,r`6[{rXn6{&O'w<"jkMѤn&axlp#v1dKYjkͤr9m1#'߷OZprZZy_",vٔ+ Sj\)8$sz]5̵7u- 9>MŝIm Es8Nyֱ5QݭH. "{޹v2,M6 V JWkr"GKzR60ӻЉ,qOl E"5NRSpZ>ұ>$ rC;)]3á~/Zm8$Wp:$% %\n4H.e- "6\dzT7r*j^R2H[J:Rm-^- >`A>)65%KCLd~F|F9"tʖ6d qwM5t\d屪I0K4fؤnp#>ԝH>36ՋZ}M$Q dgIͤSfvH\,O>3<޺aJcsqԄJ6g OٌA# /'zʸ:RR֣m72kQ7;rxz}+q=ȮkyB,Q68 x&T!;G@YNUl{׻*4#u8JmSዝ(FR`;V6sk乫TnAfQv6@nӞ6 xiXMթ1^5]#޸*EpNik '׋*7*9#򧋜TJ-rܐudbN)ďx9kMl[Mƈ$ʓB`Ns[#=zWs6fp<ۇHWYq=릦_*KcN 6Z'RG<\88֞ۘI\ZCL%i4\e.zz9FJ|[SCI]43qx&z:RV`[oc=B(Q܀è㿮3^ C$&Ě6w ,Gjdxw88Ֆ.Tg.hnU(֐b;bb!2!$#:n"}CR43jIvfGi+gaЗ"2j?ioE-"U<\ϭgB٢ϪȈzu %=⩣{D[du{{mYwsM.Rtەc%Ub2#%\g8ڲwcV5C5"x世5W&xJ#::QQMrY}ԗ0HQ@O'j7*X_ $@ *y+ʽ UPrBrEYE-B% {dz\!$nݶkCaoDcF; {vL+jd;]GqFʙYET^[QD{  g+Fhn^w|A"BBz#~s[Ev"V]QXwMt.Z[].# &T] rM*Ö4tI5{ a9e*1jq] H,dh4_*"ץAU5*rW9;{׿%[{59vc+Wv3*-ZڝƭswIp#ּe47dk:PJRE%W1#|ye?*gPJm2Y̚١F8lr@viJѓ[ vXۦ~LqV_::JD$V ޻Q#渶I)LpWpwx*k#K5- 6a䈺JAFy>د>NuESZ\6h>^A"# .'ö}Jiz|ܡ >S3*cW`rW䋦ծ?hx ^;+jG 3\ D'8' iFS f"$F`ʲB9-V2)$Y]U" qzԯMW"ҳev-"z㪿?̫oth 0q{pfΔ\]#f"⋃a> ϥ0AO#ژ ]1B)\,p=A:_A4@H^$>@99jq9&݌g eI$ҹb[3Zq̗E+IbXdIdۚ+]S(!>5=YbddpN~+7̊XY'"7|0GMڑn=[_Kms c-`mg=8֒' hlxR %{s10} ",ܷ29I,WGVN[cA;Co<^qGv+M>O|xr$)s Baq|!0cw쎛7Fq]ML(.F r}ctJd78йem5r3W:!i3ݳ`I8xkI-&֧'vet\$JѬ&,+*iK|RJܩw/uҵrb0<«(I$Z*Nm+ 5f9<⹽Zm}/h^TE u \qǭB猼5e>6v1ݜpUs>ԻI!v72Myg &v!p,yLNV*dHW }*׾Lwh&;!0G\thѻ1]K<0꼄_\^'dJ_W[lO}1x|ܠi}{V(Tnn<~59icqi%TȑwSZu=pBӚr"˷H4`ٞR0?Q\Wg:cBKBym20ONС9Y83 ZjdW<(Rc)ր3LP%E=d9CPqqچ v E3 a!1>j W8;هJp=8k1B{9U.QUNO0)lLha&1I1NAjB zQ_"ą#aq 51KW2B 󔌪mU#9zqP﹌wVXآ: `z«z`kv܎ TYu6V&@ s\)fɌfxvYθYd{ \09o}G YgK."+~AmzH*\zW  1 c$*p:vS'mYSZ4+YA᤺UoHk7ȟb圞ych$ #E:?B஌ 2\+BG&9'o~+v)bHtۛM2ΐw18ҔcYf{5Δi3)s `&9sӨڮ4UNC7RͺI泝#n'NyI1̢V4"V~*3}7-u[HⱙV N28y٦&^$7.ajq$@}EJ-htkZ[aw Ѹa UҔcRJI=kT LE恜`w}訢YJ\}ͤJѠtUh"h\6{d=.Tӂ4li;_:- {v#ұrZ+|NV,hZ_Co4m'p:sNΈi+!:Lp/w@zҺ{44m=H-cFM:q-Zhuy+u2`w/$ʻiW7x^Zg7hn/|#o֯u.ʜw-3Y`6_ˈc zd*u%ѽl5lGndF &#=N܊bݓe^ H/.ir^H N MNutgKr5ʰe8'4anD3$99rs9|C荩֭BX7;iTAs\:ϪKYAEKYL P~`<Ӗ&h9٣6[Bͼ r(B9=DO%Ocy0=2d;һ~Ʊmڤ|ޠڦn.i[{],0uH?(9ke-M64Uаgt۷\*J6DV`HK)zFJ T _CrM;S=woo芆V ǐOP:v4n-WSȱ2o$.9<Daizgxn#d߽..S >+'(Vm5Z'SƆtdRFkHX~@t~ R;Mb)cefc1'$Kjws,I APcT_ejKVUb]68yI.yx}ӪçCrӯgtnV)#m9+aqZM%uТ^=]J bTH{`<^b暋Q[:$MB9aGY wJp\z9 -dT5';J\c΢.[5kHȄ<4_w<]:stkDo6)bV$SŻIZ{{^E@a9~(~:Z#k.n8p0קޢQٓy2MjDk=LV*C m9#=5v+ —+qkq }n{-BW i#g P垇W >uo[d鹉<{ҳT1hk x'o!c`ga5$Gc&uK}8|"@!B? Vl({-.r3g3 DnNTq(M+T0w wV@OvF eKۧS9ШҲ7PP\G*64#=*#5[hfڙ6A1vNU?{*Iꝍ-L5t9Dm-_+bp0?*Qm%K֜ڝbD,r|>T>"|kck " $P3hu퓑{%(z\Sqƿ궺|"А6PBI8E.f=bY.OmRK:hp܀?Cڅ498ba\xW[-n`*e %H87*3ezcj `)S"`z},'y+E|i6~"ƬnK1Lfv8ڝa]WP1>ՖrZݏ֌i̊<4L pC綹xj&î=~+J9cփka."y ;C4J~1X*R<ػ=:l"T*2}'k96Tu[k[d+j? s'ӞӅԨW;[&M0]GxU7H'i9\#dù,m FݞH$^<#p?($JcjVwzRi-+MB8\-;\5=ټa20GC呔7I W=66Rݎ60-o#ͥx!bP0r-'9|EK;܋UsGvp9u53|] J6i<\ *r<>1r]]A>aCFpw*`kSfǔ~~}3YsJF%tkVJ0B$RK`x# t=\\,FHԌE s^Zv0׷l&YjaeҨ98\d`q^{TGLOBdX!L}TuZ Q_<|VIh'i٣b+ĨBST쑥ͨZ l3Z:>֟'7ܶ5㵳*ϙt} XwGeL2۹w?#ׯG142 Bt덭"^7W-UQ9t'+e!}߅q9o 4\,ǀq֐G*1J4 #JA8C1"{f A Cǭ騤($LgƄhlL`S?Ziɦq6ҋ M &e/#u(9* =?+q\ˠmf4R3y`m*gZ9Sfn]?[}T3>Y8CBb+IOyA=$ :gkGNL94f6CY `];=-Lu{K{o&HCKtFW՚*s^g['md۴r]OV`a{=1[ҒwJkK=YUcW߱s?}j$͡U..;vN.>e{rWȟ}z>\I Ъq_E8*W5\٩ˋ;a3+b6<$>ÞF-=E6|A-)FwI]ğ_O?kr{ݕ̅I}##}O]ˢg_湖PUG @l[FrZs/3שEa#5>Usۨ\wm3HnuQ)̈́(i*>eNi7sy%x-Ҳ+(,8=F9?LV{;>nڴ*^gH+9a8jn2W;>[D3uGLg# JXn65-bż#Q[ gr}oS=J12[[S; .FbRRGOWUfޟ;DJa#G؀q1ۯVqm4Q ް]6GD1a=gԤft9x˭ibVEv2R8 x+\ٶMN:%!*`Ct9z^7{=ڂX*J@doqk[ȚK`2Z˘+e6K[[۹2oR8;c3Qiv̋{(h˪(nWҦk-,m-[6F6Os5޻]_vI$l1" zOT!cgC.Ngn~t^% #sړݢ7Z+4tmR]J=BQ L&י+X!&o/<IX>*O\RIntO5ƥyJ SEk.]ȟfi6mv/o |#θvnGN?{}͠: hbwqo'aQröp*jǗ`vU-ydRciK>&4ĶVm̗F`3( ^Y{u3ֵy5ǧ%-SFrZ66,8iVpN?=xZF-AIrţl 8һ)g{٤Xӯt$6ѾVс38YHyϽM)f]ޖ0ViԦwLd4TixoLSTZdqzmn>ߧZ[\qB̼uG$s\W8w xL,-nPb?j0v2MXl۰ `3JꋌuDJTt~W#"9!ɝܟ(9"\ۙ 5M IpxLx^ t#͢fa1aks r;qZN4U!ʎlGiws٘0bOONj=I#ZY┇RG' d[ 7K3o4}'U{>@<NG"WRwFږm~&:y(*C+rIcNe(JMV,Zr;-.땈#tyqii{(n1u '` OWG &;곌)ee-pnԫ(#;p7̌K VϺVi@3l~fRhzl7$r4'9g'<BVoọ&-dq<*|@'ppG2N(a-9_bF #1AvPvXB^4!:y6o2|qTOskZ k[[Sˬ\!Bbn+9VM{M]Dl_(f=Ҕ% CM@le\sҴT)Iu7I/o<sA1aRSڙG[lXTT;& _ڲ:I豪ϥ^BmeKy I >߯zP} ,G'cqw<ayi~5QՓ^*q6qLH6SQRvjZFGLSB8P& zP-P9qʀc=)( :^:.tQHW`(Aր197<ޙCzc(9qta{SBb7@هN=L1EՃff 4Ac)u;}.td7nzRMl}9754C,#e}uCZ%В&6c0eY"bo.5U;zϥd,Hm =wX16|S+-gw]Y2˝7)?qAZӦrʦ$֚}y3gwt`;bĩMs$w" IP7Ij^mKqiaY$ ]0H_j.ګD^_0֝ZvmxMs֖Z͜:ӶdRH{ǿ>7ʴ:"MJ?x#_tAs:te=$`gAbwg+u{wIRH9.N]+mFyGL u%ݡ|k&& 6qQNqJ me]@pؑ{֮NOMihY$hemsq|gPi%*oR/-3u2SO~ Mtd~xkn۰gmd߀:*WfK\ơn,o_; nʰ`]T[/Gܳ_$R)V!U$Try5lj+{#Ӽ1p,wyA2s.V{)th^ɦ_^>!鐽Ft3ms[㽓Q" }*גV}$Q/ɷ/W+IKbM*?Tsȡ#jPUW~IY"r_;>ӭ98+X>"o٢чwr=3x#NIǵ67i| <9[of9+u)?o)2y%X8Ϯq%OC;+]ipm<(ppy qOպ3fx`$h7 q U;Ź4NoՅ64$:tp8C[;lLz[++8gUNÔ#ۭb&9$FkowGG#ֵEft8Ω2]D0DARV|z\c$lj: SF^ ɤ6˅0>FG^~wNQ˿X6ARH͜F$mx~64[\Ʊi' n'994Tbs=ʷD^ avO8#ӾOJ츸$ij2^eeԐbhH ޮJ@[%tϳ^J-H3; ¢5x_SfL:0y޴rV8E]->!&fuVNG#=q֧ [i[ [*]ħqc#hօIvKym\5araQV7B) L '3qOS帴ϸ_»=*z<)ȨN:P\2aW~}iPp{ӕoc?fl%BoJ%6"J01҅ҳ2$/_xr-gNZҞChd`2"-jRkwq{nI$gHSQvhI2.5Fb1.AB`w%(F ͜7--򣔦ߟly'===947}PfԆ (_ $;ЂKs_NY"{mΦW* {q46bK rlfy0wm9㯷⮏+^J/hV!yoq1o%ʬ\cH=JM6f{X"P XE,^ҷ"ϖ;46Bn? kkZ4ӲFM:9MrK^zNq> :ZuG)+6XGc>zRV9<]ىh]E.t{#:y}+ KunKF`k̚އTI"F6{9Y^W6e44Ar|Ec*c9Fy:t5t>o!fp_~[d4PiWQMA]꒼[Q-,Br]2˅'RvӋ_L<,,BxB1O'*km-2j'faP0rAvCRkSk< d6Ko=eusЁ ZQPVuƥqpG`e@fD ӽS$EB s:QDnvJW,Žy瑃әko^c];${Y5TIP=2rOZjZh4G߼ɩ^[wy@ Vnrrݙƚ ش9GnM,'>qq݃zÞ-3\b`~\Μ\'f MS[ݴ\_EQ}8:cO*{-dHk&i 6mҭy+QN*z}B-D'~G^yǿ5GR9Yίd6ieji l9 C3WF'uɩ 3֧݁ȗyu!4W^+Swr8к9I&~C7دuW`߽NMƩP'Զg_LjrT*:SsUI^%*N\ithKhArN1ǭ4tC~dX B@qE : bL3ҁ7ijcLBI< q=sBr07 \{hdٽ;2=?k.^]ee{o $ dDKӷz6(N2w5bD3""tu]22n}5}y7aZ+Y̎N?Ք, ;F.-%g_t =ÇuZJ _fD$7,|0]H6A䁚j;)Xjr${ιAm$iT*sC Q7sE^.R+iv؎8sȬ}~c>e{iZmۂ oc8Ii/٣fWɘ6*dgȓ鴳Յ̅Q"ڛg;.0{V6idn-cIRh+rqo֌\z%N~rK)LsYjt2DUu,X(Q9 Mr՝dqy 2|rEy!)j0Rw7.!9e-Y35]b#|s<\ͦE8ݣ:W:qka]6#N09/Yi:EsfXh zSs7 TOfn lŏ2ܹ&2N g#gQRk#k"-<-2B1so#-yZGqȏ,XTnA#5,:IKsn?{8'w}sAG'9?كA-=8d lvF$»)8 e)Vkߎ/-m32"1wuQ2w4w1{"A͍!mrvpZ6bKSFnO%ܪ,v9Nu9tz3Mihi6K[bKv2HA p2:r+HҔ[U[EjieVkCtHPpたӭaVМZfEC,oc"$wHA ^p;5kvn2;,Ks7rr3zu8xVt7)_C`^]ڪ:9qxsAK]HR]+I;?Q^2vwqc̬OIٸ, Vg98?Az%l4HWK%>}1;"+Qާ-UU_֥b=QM# yDX1ހܖ 8;$Bn6fVF|7{]Vbn@U~B9(*,tA RJvؙ܎P!1OiIؘy$|Gl4 p8}Ap ir^c$ПQ3ӊ{ ^cp=)\~ @^(S@&qրB`LoEƵzC8+nmB&u²?Aq]m5-Rj!B\9g8+ֵq%NO.;Ym$p*M]J:2 ;nHgUfՅ8ei͝ޟp,_ }S9ws;4sr3Q,HN%qE"ĭQVQ6-t=Ѓb_}֪ycIy,@q'E9ZGKs)&l%Q1AaIzI+{ߑZ2ik ]Y@NAۥfhTHLxgYK˂m1idͤNrX/d{_lLj``NumEݮ:#bH }1QR&ގey"( 7)}157厪GiݾG13M q{0簫SˣKNx:ޕk^?.1gִa%{sún$QON>jQtѧ̽Oésu̫čp16=sYpj.rV&>%e`PHԱ$dceG3q{N+6Ѣ/1$Fwrqӧj&:r_Աi 9odԠY<I]ÃN4wHzAlv[|<ݳ曝t&6omܵK:oأe`vۙٞlTy$h 38bx*di (\^)sV;X(-9,gk5IIT dڕ\bN-Fh 3j2qt/g\M˓nec9˩ӄ=V.U%.V=&ݧUF42|Ì~+p|wӅ}6+5xgxdFBO#t5ZCzAs4ʺE#F~P@$k tZؘgk0cvo1h'0bԴIn!'Z囻6&gֵwAl3VݑA(uM=HSTܚ;D#(c[~tpF( :,p-^S#dj6"'GZ1`:`u-q (h!LA;'1M #~CF=hC*Ol#ʘ8<پS۳+Ʋ^Z}{j*2$ *2[l)HɱDaVLg׃g[kfP6gdb$G?Qzjzm.m H:2U׌s֣ґ(ݜrXA32#\;K  <~)hDTPm_Vt6qGdC{k;=<݇ݼ 8 cjבN/MNBN6/"p8)MNV_i٣u;FC n?5FdFڴ^vYBJюlULnfx$r"1y뷑>^j)+k\B\JH$.A=8=jԵf6˿B[y1 Ĝ=5JksX,Z({`g> +*ll鷮'WZrFAON3dSj/SHfpDsyA[M [}#4`x^RW +iKI~k4DNOv ui,%zqҜ7fZR8UV!I#_ҹV?cM~++K14RBeR29ԎGޜN;+_,R)+f̡Kq$oiV=xJ6:%yi<݅x9eUr+#&eWBdY2O1S}cEMfZk$V\J_3=8U{G3mjED[@$=IfOD\/ЖU4-Bq5õY'%qSY-d&n.⌻Iq郒>aӏ+((ߛqԯ&wYh0}/ =ls^KfVVu\G}|B̠<`#Yfo4Eі;Fċ*<cs5n5Y!<؊ipH:^%uXQƗi}\&ؗ`sq=kX7- qM{vpa4D2Mw(#p0zUB+]JϹM5m䭵TecL(uqKo6;VQm9;.K 7]He$`tޖ+Nb*i;]wRYyv`pvèZq5is&кݾ^M.͎T~?.8luP1XH mn!v ]oMKsG)G/B 䵸xe208+Ҷ49v ݎ8@9I- ^涏y yku`HRONQi\ldtcyiJ+0x tQe;lPZZ۴)=(SQb&Rw5.5qku5CN9ǧӊ&ROSK bMx,(M"T%@W;v6p$r0yޓ(^|[K0Ys?COtmMwC'c/ :O\ qZ[e{佖KȈ˺ܸ=^TLhr&N7Aq*Ayxp{3ֱ}2&[6, p[~ͧtXt#b.Jvr}_Njna/SӮ[ZY?FP z=.rKt8${-HPzcJIX;YH/%IVU$OJFб[RxCLgL5K by˂J.TFj\Ci471.K8 yk83э'ctM+mm5{,OB~sҷ[\'Aiv:^wICpz늺pi.W&aO2Yd{UݑGzb\uM4ZdoEmwX7"%^G\51lnmmԐ㝇aā{VF]VuvUDzTYEl5:[D !2@cNemL~I4qsFNtn9NErSvz(zW:Z{-V9G -~5VaUc:3Wئ׸[MC3j"6 vP WR(tss̈˴_?{oi^},MZ[j>qR,zN=s*j&\ֹi-MBX{Fb#R}5VSrQkM >3E{#1*$Fsߑi%ʎ[Vfky%+9Ysc( L^2*t)kd`hڍe dyjr@89TvҮⲲ˥b]n*QM^v]EY[J|S:9#k)is|Ɖ/C6"ؘqc 3)\wߞS]Yf' nE3*rԯژ:<g1>v˿\F%VWwbxM3r@~3W [x~9ܤ;*WO.{*jNsZ4XIs2)M꿯!pZο(Ku7[љdwUwN4b!"ձmHdd`1m`WZjFOsBAΣakIJQ#uńa}/5ɒVH'?)VRPd'#?Qi!tZ6'U|SiRJ#[pC)8"ij7ik,qdyo'g̚bkBk 6kˋTC#nh}=*eMMmNI!̎UY |qqҜ7Ae5ʺg*pGE]l0.ug"y .Ib\]-IF[F7I뎜tSJJRc-`Y dT( ^ 2:U%k'yv[Cm55y>nc \?c9)ir4 K6/vBEp{F"9#̲X'HOBXT;m Xt",:!TgH-p2+4.ɽR(.Zg$yBǯ׭]EJ;"zM̜e'C:V^:'9!hئ >ҫ;Ŧ}\_C_4ۃg#pz1(+s~:v*G{z֦rCD;GJRWVSv WWRM yI7ParblNLZ6%OផXlF*UEJq͸ 淔 WД85gLZ~9sLWNO x`8Aց\ܞO@ d!@z@B-qOHMyWFE&&uO*&H 4Qs[(LALc(4t" r[Wk?Wdu`ZNqz޿;pMKD,zmα%]-N z8ⲓuq͋[\_Z\1dG\ufDko%˩g MdPwPr01޶JVtfTKgp-lFݣbFA$Eb._Zm[K`X)WX8kH2OⵕnдjB2Km,:|jkFvFxKȤP֚rKBRY$nbnj:芾ڑ Fk;5+E p '~W?bBYmyV8rROV>v=QG1tVYo`Ӭ#uU \8n"T{ ҉-d]A78סֳQњ8YnfOե"$im*8S:D3TyX08#U't`gP.{]h&MZsǑhUUܐx>DL&֌t4MD a- ky$'<*oə&y,/3O&4d=2@jg'Ck-[w:"8N@'ƉǦ;tM=Ap.n I#ޕ֨v+@GrCq**G$fɉsxy"ӄt+ӐN|bV)=ivMn G$guG)þoqS^dHm 9_W֨yGdvZ)KS$HNYV#~ptj-dHsyv PH8N{~T 3)ؑNJCa ҙ:8VbN(C8=hw=]@P (ȠI!P;fXJV]셰iC@1WqCt&IXX@, h+^4Й\0lc̛|sp.# ~v 䶧-娖K zeN;gs[M7*pii&y#\y1v'\t%.Gj|=[E)>9@p=@{UAiZo XCm.Z5qa $}+I5\K;cf#q8/n}+9]/xy+_wK i"]Aq׵g(?tɧinygmJ-R FY [=)JMXV!Kc)ac\16|_fbХNmeXV ҳrodk|}is%4w6gX̲g#ƥ7DmQ&kvTj/a<^Yr81kr=@,XV鳒ہĴi\I#ܓY$wpz+$Фl(PLHRASo1ۯ5PY4:]GxQR 9V 8jM'e=*4'՟P!Vv#$ .Mܳr"QݤFFNF:7)/v[nji6̫)m z1 nzjHG7͏2@'W--Iy2ll.EH<]Iݍ.&` $ Ub(ciA g%'c'uO'|X'Kѱ-]xB}PԾukĉJ1N{IjckW_[4JtRXc1iyrehx;:tMR6 @=3M4v3."kU=[j<jEݔʤI#J4KiI*-sr{} .G]Z$$B8I\G+ȈB\(gPx GQuas-·KH$% m$cjӺR]M+΅5vQmFD8Y pA׿ewVSѢ{Kw҄UB 7'!09ҟ2ɔmКogums++FֱRynIÛ}UlC ؞I~\aőx$ i]¦p\'*a;n-VW,k^[wu\ sq3'8{}6$:itji+d+v0;cc.6.[?ܘ!$d>㻌PФUK66p,3 %zdcs*Hw*3]QmtK[33-sӧN*M WRjWɆ@ R2: =J졭Tݔ&8eU`A#S|nt_vxDpp1nk.e2].nZxSGI#ϴF9:unYSjcfoPb$JNMkN.5wVl" +Iƛ[jR3%4m%eqx2 ^98r=GVHN Ԕ̳E!" 0)⑞P= B14 y v]: <‿@@9 lgv'lr_sNnʵV6-s7ot9]žv'^pjF헎v[g`q1`28c[pjNSV-2N_ɹm}"=TN+94zvG"69hePz-t?2è ,ScC¨ۯjvі4OGOPtBUTW89>+jnҿE|Aqypge6]o r 9W(KHm]FSe.cn 6:z}(WܯEFխt4h?ʹ6St.4F+k]F  eFH4_Bemiٴ]DLl tqG$zVKEfr^ͦd9o. x%F;qⲌg)^†llꚌW*\\2+%!1-:5oG+畦4b23/̀𠫴g.D<̅n}m},I-̒^g8y]GCcӨQMji)/UeӎHǡ>&es,-{k ۑd9([֖4XUn,s.ry랾j,Ufއ=zI1!Bdu PV6t!d{g\-׺IE$v7gPW~M=+[\ Kr6YO=y狀ʒ/069x-Y=GIt>QrZ 3Fũ:cڌ3quJ2\O%.Jv}- 1>HVp =1ަv{Ԗyah#%bP"9'܀Yh=Hҵdk)vȭ)}q=oNI)c'khoV/.rWҕ% VkM=/JAtsI#ק9rfjriK-^dG$pxW{vLTbXE3 mHyc$uv4V+}VYmom6ʌ1PЅП졞x.4&d[mGs N;&+GRNع{H$ėp>Cc'#p P1y뚜{|Z7'9Jc[VA'?xg>}wR:wgR=xUgIx!WN1\TRzciuչ!ʤ$f=@>ݪJqvWE_^eǔdwl1Ѳy^jTﱣ\xKZ[;g%/2zsNu㵮 ^f<:G=Fpv#TY3[$';H.di>7匎@]8FW[tHR]JYQu!A$%{3EzIjkCv{iX_3đJ##W ,UX6o*P}_x2bi!rpzUvꮲ_ XZshN qS Yڐ HE0NŽҐn 1P.xn)\wR)FHBcɵ =ւXW{Q R%ǵBt3L{1M<`pװs@!N(LV R!zSt3; mVbeJd k~)r3d\8 i&UѧkOiOkiy Tu܌WԌv0D% F7HX7`9~K塽f_5Uܲ,3r䎝c޴r叺U֯cZT{[kI:F7HRr{sW:C6uh#i} $[r\[I]KH8e*F^ Tc;)+зk$ ;p>) oc_ ίA/IA'FAMkY-c"U ;@*TΪB-63B|$v OX< -u{ Lf-Qѣ̡ZiYBobX&2zӰ Ahg ~qisڄQEw9mcZN<͖m:O Lm*@W$ }3ޗk\"htha΍<ǹ]$R[ PwnCW1Iq`ϪiFtDTִљ=QxQW3>[FDK෹֚V;Vr6;׽fV%'Aa{SJ"αek~xx$WD)y'YEaaj!,d8d K&1 ӷ5ijk Y{K?[&l-eqz']!)"15B$ێSNa)E] "Shp|Lnsx#XϛRԿt饲$L4sz# +)|'!tuO[= %1bF39ZӍ%}KiGE.Fs_Zttj"(5~<cb2PrֶX4 ^mHbAFYpJ;S4!AŵQ$Ȭ#*\-wsi:vj%@1O/͡$]4[-lHBhuz}4Ʊg,eyd<J2b=II .o>{g#^~%rX$hc>ټ_/Nzr28tenu[KSkxH7\FH9CvNgvsi/ʧ:A6Y $gڢ>e %wM%ֲ'8ⅰ $L氠_;JYerP qW[e{@H.L4+(@GnI4jtzh]G1kvY0G#^4q43?ˆ//֢D2 ^=mRJNZMj^ d8|r1ϭ=r,r)WL(JIfc'gb̚)Jn0m#{:\%XW'k֍JU9s-)IV.4l<[ kwdV"2Fj-4 q G_8cKG ޿Bʼm g޽ccO8&zHvBLI=h?>(&l}(P&((AGҐp1!OBv>j#Nʁ&3,!Ah7R.dYY>M=bw(P1$Tg݌1[*;6H Ϡ==iwv3isK]652y+s!y֦RQ@5ha,@p#by=*-).]KQR`tBBI$BWߡv֠]Ge|[Xlu7K9'&tVj]Z+LTOKqLQJJ)+s#[H a4laHNd9eRZ򻭌+{[IYHC CJ믡rh$^com vaX9el^Au+l̪Ev7o$!1Ⱦp6W.-J7"ԭgh쥙BG!mAAz9Y=M}Jbw+4h ">Wv_;m< *@Mď$Q9?Fs;\Vv6īsl\\vIQ9V[V>Z[b)x!<9GC y=[n.+#}zK)j#hgYcϹ.\\I?dLDΤ~zzV.6!00P\ ÌfSAȞ[G[-⼞;xA! TA0W/GH{m47={-3PV%\\){FΒ̭Qrsq9Q-C4KWSm9b/D% Wy|lCW^酮ZKs-!OְAq&&s;3v][Sݍ"݃ǟ2y0 t=*jڡmHF6s+o[AUEO2D$v1äZ{΀t7M6\Nҏhk"σV7Pʞ_$sGs8ߨ9K(8+ջ;R܁8䑟eioovES%Ŭh1 1Rqzf]Tc\)`@>bpj*FVeb;K5*Bt?" >4S'TCzzw)g ./ "G[Ks"9d dCG`)FOZ]qfziB_d^H76erG|r2zVQJ+z}"TX[z!q( >5Zq֦EEӭwin%.zlא9YNV]}Y ym4Vlwϵmb=ZoF*wsY ;ͪlaH;pZaRIJ8ڢu }Qm-1X2XǦqtssmmimY0ĭJnr܌qD[R},rRDi%1m 圞3TZwbwv$d-؟F]ع7[ZԋVq7F3ZVvqZ6sŲ 6۟Z”5'H0Am{k՘(R2LxwF*'K?dA}=鼕Y@!0 8Lz8lS|[O#~Whr{烞9ZT:)-ӣ5vbav)_a2 ~c@41ҁ-6 q Bcbq@ҁp}qMj-ṛRQp .#<%GLŵnn$sp|A sӎ-ikI%^ӵ*#pxG5ͽYSY,7'l`yu A=Td^x[6 xG6VG|N#tBmdjWw,wtd(`~UWtm<`lf@qpcӯ6Sq*OY*Z\VO+dNr [uHnZ(i`[kyH;Ode30xрo<9Uibni:w E#A lD[i n*S ԃG]Z-oX9ٌ:JfNO#ZY%g>` v ),ķ%+*_9tE8Ф<3 yK혲 zuQqٗNя:y-iy`&AϦO⧕l5M[:{S!M=}$rad9WjyQ{E'?.4]/bPTךSR(oU$]6킫99\g=ҵV6h^fF$l4n? 5ni:ʲ0`?JuD'c [q H2H@p=J\\~My_Yq<8ͣjׅܽQog5v~)2*@k)BƜatfk"D`<A=sDa}1S3V]nxn}Iev`fB1㚹8{^ZZiWMo;F܈9L sJ!7myzVlI46$^`@06iGچmz[ H ;\b|kGS#6@A2AKn'AFNXĀ<Jɶ\ܻc0]:䜎;"v: }ݕ~yeT cߚBĐkfqA;Ұ)Id텛ku '!%H1OzM7&ݎ~DI'Gk9Jcfu 8 ։՜tWfX*ĉ #ȁK=c;;GV୹WRg:ɀ;fq''jI뎵j;ŷōkW[åOe I*A<.?5"mk i~e&,ެGoJŦޚ%&8j^$:2XjG\㨮J-YΓ[ZBq H; bT Ed\kWH%;;kvl5w? r6O'zu*msm3 x^.]"q0$#pd܎լh7kWZ-`K3!^5cQѕ6^$qSl: Q\w/&hY09۴'NL{+cB _m\Z#=]4語7m~YM:b'kat^p\ckuJR8|>heB7B1j''*/C*ÖM,dbzgrIGCU˚t2yp" &9'n|׿OgmGrX"xa >\Gj8_Q]g(5"a0zq4' y 1\B8!7Bh@(&N# v8ծ2}lq_J޸F.:xY^OQ{5Ȅ:c:zJQ5󧥎{\-8 B01ϷW1;3^񵦯IC~$XXQTyCц! T#S}F&i.`d9#BTv+:]QmHQ`.T QiIJJQOCnP׋s'ȑ }=)v!-lr6EZFl<VV-S.5\$>,ڔD`+0HI2 )<1jIO-gQX(*s8Y>M#ktzX%d@Ђ$3#^ʴF*z0B4X<5G{i<6- '>K#׬}iRq2D..T>Se32edNj7 DŻښ} `}xcHS`wyڰr$}>JV{9Az~n0G-ktn%4oK)$ 4r>Uoٲh4q(o-2'0Fd-o#$JۻA)%x(k~up.t'sϿҺ`Up@8U\zgE:*Q[m2$p8W~knrM8&VdIЂr9=sM'̣2QkBoKy;F" %U|sϵkJNkWC]z4Cwq]ǶhvD#aQ+zٚyD׷ LJd<`rElZ4of$vq_iY ҡUp{س-:YƧ0*=is6 v0=H$ۨNWjMu5(-{2lEν$5Зs.rXqZ1i>dt'')h$&Yt]4CѨ2R(z\`Xc%wѭu8YqZDF h9P9jWD~^kbY"6 tnÌЁObk+wFA얳BJFn>:7ȶK(JJ$o8r0GXFѴi?ܖ`͔Č8$$'ۺ8oMmݴO= ֪ l ّ6w grE$0q*'\ZFHlLF0%y; I-1X5ޕm{yxR'vAZinj6Z)4SHʴ|^¹BoXJ\ֽlߥ$G<~'zqVTiNzK @0 u8'j:Ҷ݊mɥ8Sm*Cos,ppPn9sӋi{+uZnkoNڕƚ"8>Bvv_OVs~)÷zDMe<-v: Ǩ#j-kӴQf2;ː1+GAm[÷"lά#(w[tf>bVy;zz)aV;:a[xNɵMR8֝H9}*,u/CӴT& a0ͺ%cA gV1/ܓkxa|?^8=G[I#K_['ORWSl@1КpV)>KV"ZNO,ocbt }Vht:Ͷ]H>px>#ٻXtUDRi%32_-~PqdWF*_g u([hIo$qw$򌲾T~8&m(_CϿeCvB`cF5USMiZfBTa%d* eH=YRvdlcG=f}t\ڽ Es uVNm*V 8hхiNuc(+E\,pE5J##򮃙X]bq= RKm'2jKpZ4 jTOj#s7X,nN]GaYɱrnKRf@ ùYPuUI*0MN"2ʏAY*eED_MPH x@5vDǰ+08OޙXp9sڎUP=]E+npNV۴NJ#Q#3.7)=W>NC>{S!kg33N@QZlvxJ9wxOr( a=@9O])qWVylq :_YXine;qrk@{kBֱuӮ=b;To$%m.ㄙ^\oBV9kFU#͆3S?{i:R,g3cǥG:GmZh)4QԣɛHpc'>'tRIFKл_ xrkhHo݄H|o b1_¡E_ͫZc,VvQOw5D@='c~ij6л]<\["c 0'$q?.-ɕ7k9-ZHR2gRm#4FJPp 1:QMkbܺ"[ZI%o-v#.r$˶~ONcӝYah_eU(3]J mO65; q$ltphW4޶:W1|;kZ=Ӥ |Gnֲp薦n ȭy48 \F޸+E/x55emKša1E8&U >SF~swc9t@=1ޮ㝚:]?Pm^-&y'?\};~oD0ttZZ,qjo*adx cy+zIMt|Iv\Ah$mC܃[vT^fk 뗁4M횽ͩA $C ec{~t= [Ksj &ct芑S[81ӷby)GV,45L&$*rJtSZ9C#hx/,cҹrZlފkdQ(oVMԑGRb77K4ۃ zwLgWFKhJ/e"#i,@ tZV26tDO ՇL(޽G=zqRݺ#&̿[=mgG$Aܫ$8_к'-Yjrn݈8;BcÞsYex61[oa%%<Z*6JrVl3j /u]AG`d@㜊\{D]R[Ne d18+0NQB;XkX[4B8;8zqP6[k-$Ҫq K1*G*Ni h М}B Nsxϩ*6џyeT:1dl>2rsy玕PjI^~gQ{[h4Y@ٷ*vH"m._Cl!Qd$ 2pGKVE'u4Ҽbv)|R{'7NQAhכtct$\L95k>'54dͩKsQ;T (J1vBqsZlm"G.Z;mYˀ>{rzbU#wRMKZmֹfKp{sN5ٟ')R{9;o('$V#<=?p=^ :]Ȏp 2xm.X'֬*]ƫĻm}z(X$~!sqj]rfN}p& #KIVmL+ɴ:z;;ў&hTӭig9NG1#>UgN|Q؎o C%흺鬤*6z OzH;ğ`1*ƥ(?N?:EՑ =NO4T3`  qgL:SqkGPi% gvD ANgF[g=JSk>uǛ&& NE<5Wٛv_Y+;K.hzz$ ms 3:|M4@a#qG󮨯wDnxjz|:sxt r LE;-ugwV(_cӦk3FMQc++E. YN`tI5seg\#BP+ CI'QY"lNT Jhx:rF1Z*SVob n'RJ$:p+8f6VoZ;ڵJ,{4AmKP/ }= UyOZJ~2QȤ5-45jjD4[ѥkr vJo#r%]Qp 5c+UG9jsi`xEh|UK*dxʮoBf'bҮ5ҮT#Pbl}yZcN5I4CdHq{nX^fnQ-Yͽn@+)[qYҺVq]NPCHأjTں W"9Ieyj/q؍m:5a[]*ڢ ^M]d+*I\I$, 3jg rNE{dR>C%9-re:W ]_ hڈxzaSLA'ژ9zb!A)1ٮv>TSNC/z+Y.ǎټKs609۞>RQ9-.4Mv)͠.6+Gesǖ/R/Me9imd,zI`0x uۚԺ<<'[þs,#`:T iByo_?u…)7cXB[G-{wmmW\-$h{}휲 rs*N-=-"7X90֒JQՏM÷=)+wF3ߑ*ٔ4-_Uc{lv$NlzޱaPoM/n|[#.?ZgNukupEقZY|u57b*"7Zd3ʹm>wll2@c'%(jMdo%2R 8gs$ܯhngpXn?+kcK}1`\y؅v2lѓwF>..e hiel|8#>Vٓ֍vlf&l$.1}e ҺYK×Bbءh`j蔥%gX1ӣE$09=B5AG[b)B>X#n8 b>8WSPǜ>ղ6Lޟ".A v)B2%2/%nYWĮ/r%Y .$<:u F+nm]H -t <{|NzQ&g5,$%p{J\_aF5tc+yBHx thT+]֥*h`c1]2)nqz ȊpI|Cjf3ΣO#UOyuT,Qb#.g~Zv>]7't[Y͹j͋xv+W8ҭdV!2vJMBM#8ΣG+) /cͥ8U..bH͎kO =1U*i&ٕq9=Iu4Rq[-43zel pB=d4h2s„3L{#rBz;zE{JhnmDG91$ |5]t9!м/M+#+(* ~'5oa$s!ԍn m9?Z$b祝y,m j\Jj˗Cx^揇|H:;!Pn!Ђ1ZqFSluZ 2h hmڏ LG99VMqQݽޛDm4Y#|q泲.eQX,g6Fb ;{юϕ=M}6fѮTRGE=TsT65aUY{5Q6Y$z>);S8nkʚnLyB'bی+FQzf&^hN5`}{bg:Hx|M#[6plEuHw/ zZ{yA'.Q'$8>*܊I#.a#BAPs1O啕nլ`i7VXa 37M<~4ޮ9VuWP hd{@@zVJ+N,2,tdy±G~՟f[ZmWobT\}=r$h;ŚMيy-mq["(m0y҅k-騣Wa,bDp ;ۑA5\ꋔz Ijin!,cyRWO>6Ӷ7fK}PP;IGtbPܮ ʑ8ړ8 FӦ`'<3{~TwB'&/}#'rb'+'vL{2;}x/c@|g'zy+gˠvKC;Qd{sҩ$}E:ts>PsYA("4hر$azq9T_ۏŒjU &]8S4ϫ6v*\֢2w0]Y->UWsJtI 3Z; bۊ؃qҋ@w.%Ȣ9'oPj2:tngPglErK Hʃ&qKa92k6TY0(lONA59d3ZLTR綼UaJ'mxw.V_2;[;k63[M,),NiAZӔy_2ԥ&3#a_1cII#Kq1#ĊU8ʥ`ԆyryH*i|W UIY9:Zkb'#+%4tddebåk[orDZ)s= ?*y6#Қ7W Zh(ځ~RkPH\sՠk;3Ofу5:]3;y=,: ݏ?O]mE{n!K.oEYc`m(ǧ5*1I7wz|38$ȩ4SZ*C \Ko6nF?T_2: xz˧C~Ǧ=r*dg:+&\~$ikilFT翠#nkT1NqZb^i4^Ov ʬ#ULg{j($r@o3yVGtQ!w7GZ%5-嗻vSwby3yN$I'y9x>qG9F Debq-Zԓ3oCV,aDB1 A):v4{&;sh3$.@ѱydЮx=i-hɒW:yia D(Ќ֜g:1Q>qA Q7|1 Zn蘶Os,UΚnH(}s+9Ur[\eƺL$28~;hlROOi (*xs+M[Ba3[.>tV:nK]=RU7v%sĞu.m+Zl;Yd9O ѕ+Z4y/ rmXd` vZ}mW.m&n<>Y9 W"\-'di$oxw@5C%n#3qܞ=h]P;*m] 2ZϜH; 4b+[]as{oKb/`<8l|px1ܙUdsΣa(#o`}ET=-VO G"p &3`={%-ESbͩéb"+)<򩼒]m6H]W 20{ fN屢R׭=҉6sӜԥFqQH-P]LXxӥ:mT[lȴTW20U0N8#&owЂ aWV V6YH q֏=$vucgQ-5DTNF$zvou9=hRQwM)Hh0Wrxh-.eO>&COKzn`}rzsg+C*jX|OL}o:.0 TR2[[յ-M `rsMrv*JQw)I  qDdSԜ9cg}6e9DkRɔ|s39}l\9(h`o![Sm'qӏƨFƥcZF &&R ǨA$sRoT9|,t?jQ]}"9 3.p 2߈Be5a45E@"{2>]r1a^;(rKOc]âٽl8\ZGΊEXeY"`8PwrխF{4/.%R1ʢ0Wqӽzf)=?lM.hvG{iV\ZɅ NFr};W_MF9`} bݓ"<gsxSЧM65< u }Uf.{ssޜEmM e-+Bn-(Yzg=='3VgԒ=Nt&+=KoV[Ե1rcqf!gV$;8śUed Re+`zr+1N)S[’!Q6'RTɧrU^4;C-8fUNZgfTmy6+ʄe7d}-Yӥiں]pyd|/JZAIIzp<*jԆ4aTqhZiSD(ҹVC8 5lw.c9=Z|Fz}"\iX|\eZ>si3j۝>KY) :t-ճA=XsRZd0sJӍԸd4ent+;F`lyNCrI<9-נQ+_\14|f͌tdUa{NUdUckm49Q$(x\>^Yp:cA|GK[omm$yA;@9z^qqytOH2(=ktPW.i= ۵D0&HyGĽ.o.ERbNAVmۏ|®s_WiDV7^$ΗhbbyqA O;OAΚZRUnv[sn8+S]tЙif+2U~kӊI]+i NC#ٶ8sO 8ӶM6fcmHDf_) ݎv7uڴsihT-KZt/5]ZdeB‚A9֋G+_"pwHT%8Ȳz Kus}xZKiH'ʹr'' q?UV4KbS&tAOG*9zӜ}xŸjúYiZJXaIڣ czrA )rhe]f?I\o|񨞏cumu}KHJh-,w.F7g d8ZqV[U;Bޤjf2nH9qo&3ݕt ԷmqhI18mg`~%2^Pu(,R%[`O+qO>V0} ulg]#p=O*:GMCs&Gǜ9=k7ʶ%̟N֦ͬevP`Cd0F9Cq٫xn;[ilD2N14TsOAɵ+[OsQ19=MSZV-eo@ndѴ1眃^ٚ8%e^Fc,"̙z= !G6+'R0V45Y)q9nTgNzY- PNïk~RxBwd}{SsV)BLP%#X8:qCfzȥmKZ3kIӑM^:Iq;ֆ.5IiIaylxֲ!M-KcmgnWGDb Aibe=*8({MWVO h_RX3ӷ+-YQ$zYj JE߅l ȋ|1A,9t_ş\5lޱK&I$՘!1#ڰIӛu[;[Z;7 ?qq[F )4TaI-o=ʼn.O'*{JzShM=amkuu\$/f_<9gpiueqs%,8sKT -=sN6\Yr?Bkqzi`@D0Xgr.N==y<.Ti^};,"u'Y_`{NQAR3@ (H9PGE,f56dzu}IYkZQFhH*"N.4q=P9(H$|:-3A)Vr9{VhrJm6*:5@Ť1@}ĎUH?Ui/#UԷFfUqt<Jo6A`7D-!^x[#zK-×LtHXx*Cm r9n}ryTg-[X%lht fr3=9(QSkD]:FM (RS {yR5y-G5cTV;dgHrv^8kj 䔕ҽgpiЋVrK 0Cy zՄ室SO w&n .wsh]#"~${i5""3fC =ҹf쎘UOVaJgHvX;`3p6ֵtS9ԃL϶a)sʢ7#$QM;t!ev:˖Y#.mjWR-)c5Qmji;X䶽U$8#pJܵ-*^VF5.KFRM_aP1sX=HV}KPwUH&v;*s鑌ȭ.e)jI<3Gejrqb0ÂFǿg5bNڴm(9 ㈂UH*rڳ8jCOrp"<9w:ө8ȕxPKۈmtx8`NSMGY KVs[1=k3eH%ͨ#̚i Hϧ?ʝqKcK×ٱ\AfVW Tt=hthš!v {o2p llmT{!)72ݰ,Bv9KMv6-IGTuO"6uu=5kduK3`沴m#А=zbs.5 }RK̊_p#i?}KjS;XY<LV%+]>n }*!&mF٭X4mX0sx8nىŽ KiZ!$3c=9=N‹Zl7P[Zu-]VH7U(+ dY..Q(Gmh{@eU~.5 gUVPwc8ha._tTF-mIF[4`qry녫M6L趻ƝmPH `kFYmuK# H d\dhMt\xq~-L謓%ǧ5 .t먫[b;KPv)c$#y#(9[akksw֬,Mj䟗*ф.2+ + |ҳ/[ruW޴bEU(VRF2e{mދxn-inXejOϿ+-G+=rUX$(!i!ܣ#;NOO]:nɟ4V,׳0U6"MQA+=H+(܉tĖuD>^hU%muآi嶎8d 9ϷqIJx/cZ+7c=-zB^1ZҺh[@-ṑmA8ZfonMG+1'#>G 1/CՕ\n3uY>ۗRZƥj^LۓqָisLw]Iv~3_Bb9K|sM jpOsMT[էAoI1)lyprr=>&383768ҼBTY#9k[;7ǻ ѐd~լ%(]*5 ?y0G|2FA|TOy*_ }F7UɅFj_ JySV pAEMsKR}sr6iB,R)ٽW-=蔛D˫ZnuT4;i೸\$}oM-,Lapv536,܆"8Sy'#KTvUzAIuaHBY,*\bcn/ݓ]ϩ>^M$o9/ki+34E}[PIcVIIydw=\Vҏ>M^$Da4L}ߧIu4U[XӍL`E8tHM%mL[XaU  d͜'Ӵ{TA9d%]UTdg8=~I\MDEqZ[߉ $ @pÑCٽ)) ^i<+,KUN >r1څAY\V7.?is8h!<䜀zxl쑝98j kil٤iPqp>3̙.S\I.hVH 5(`:7P:ͫè4C#uI;}3sPݶ6O~F}M+cy276s~9]*wf<&+ionhe 9vX~Aq9<{;sZiiko[Wc5sc#*@Jtg TzuK53q%Ѧˁ"2>P1C"*O+KK0 #nw ߥik6KQZ߮^vi(,t:X La`8;t zE5 :F<a412.zqǧEJ|oTEhskv`*pIhsml1+)a0M$J;yBi({Y`#GGJWq;B67(Էď4gyxuqxQMiU?M\atH'/n6xQZe"m >%, pCgdl`353)FNMXK#{>GC"*&ôT}{ycvɧvyc"2qQ2bw3"6$WRó~jt4NMv|w\zsOaNMlw^SOKpf8]c:przS[,h14+(xJ XnF?ZQu WnBQ p$!Бq5N*̜]cWGiJ,W~G}EG7;NW7}÷GJTĖ9K{ y֡}]oL:Zʉ!D<`zזHiEt׶A݇,8~Hw=*}(*[-}ڗذ:s0G (Feww7p5[ISϿZFZxfcϒAQy$Ƕk7>YXR)=`cg ѕ c>o33z)xzfZ궖v%C,R:ÏQQG+_qu.qA6haIIn SR+^f '-@Ҭ9,.E,8*slW%'i3}\$.aS_q}kCj cMԤd $^3g9j(lJ*EvUcm6c{tl q;uch˓z6ZI[][Dr``TrGZ!Q6Bó"c46"A'Bv[vImcy7 O~q\hC+W"Ap{]D^ʑVu՜"o򍡹یMإ.cWNud6K橐qS uǥZ3瓕ؾԁeܠ۴wӦ;Ӄqٍ/}R\]O YDR珑Ysx+&\eR4߽bTj3hڸK43Ȅ`cufl̫NjW ]F]e䴲ItՓ[}<T$*owt_[dKd`Nj9b3xqHFEJ[g8*%,LjT={-8̇pz›7l}L.d` OJNw{h{x5Ny[9s[w;L$jGSڼUUЦ}5IJZg&c$s;`~JY2F14wCW(ǚA=cNN.x{wڇ#&1Bt뫉ui{hoIGq嵭ROd`jMitm,H"b>f`A =y SOkVwȇ9+I4m*>a3җ2hnJܷvixN׈E`s 9T5|{L-01wՐ=ҷcofϴA'Q9R`e'Ԏ2ә}[7zb=/] ->nۀ2 W=B ;kۡ#2B 0NR!wvKmd * c#c0p>.i5xx D`]niM&yv 7-)GMYyff{ws5(;qKQzy&-kEJ^P  HQ2+SkFև%7R|*X 'npq+}q^4DabI0-y ҷW25kg\a+Pжv-A`*H!X^y|FqOK]DZVYs[.&r8VecB}Ns5埐Ir$í^\eܐc,+$V_R%ޤl+{GlL/U*er^5KhZb}Qb &b-ՆaB )F 95KY+]zTvͭ(y-_aٷI gP^:j”bޞdC;dlQ,U8Jf*Ru&&{O>[1Rv2ʳ\] t%,7;ڥT5vk,9n{.y#KW&@ m܍p=5q#HӔl<5smwr_-#$2}8e&vQOFRM{:2/cr2 <+e%ДHҊ xX qfA*Nn/XBNnƓȳˆ.YqKpv^l/˪i9GCT{+)!s4k#laIܵQ4yA >\yʦI]Dx^ex 442B<1jdJRt}>"ڶJ̒FC=Gbf{̧{V,Pt=xd)BJ6|/b'eÁnFB $Ov⥸峂Y8Wr698>%tiN-]Ӈ.S)y7Q## #ҳ如2zzu-og9Č~1U4)Vvt[[DR$92c U-Ώk3Ny%\,]y94QjTmcBx Ƨ̠CQq~v&wCEIl>Hq;hoJզt6˩EtaHJ({Np3{Sm6P3{<'A }Ik78ݤg$ᖟ$m%Ŵr# 68㧸涖%WvRm}P_!GNx I:ђ:/!m^0?wʂ@$ fs}oc8,V,1YF=:63]A#KM5g_#tlyg1^0vW˛AN/t2CH %q9ǡ#uqmNJ7.u-3 9B'4hՎRѱ}em.-Ց`E+Jؓ5uw,'_mm6ȱ*F=yŧ{#9IAǟOiVVw^DW<Yq8TvMNlj m^>u5gKjY!Ṙ9 T~zԕ: x$4$XrG5.)w6{kSoO4q$;pIϷ瞵9inrR1cZA}fT*΁p~ln:qU.I2Q]N7\fDt̤s pgiG%vi[e,rۑݘu5JG,۾3\ bt\$<ɓ3،{`k1N\]mBL{+!O3df]d,Fk)+9jg+HrΊ;t B)_/{!1_Iyny|y`HUr*q} gܯPےv+",;W4b_=ՑJJ;H틜¿),;WB|\$^ur"r20G˜99Zan;jE-~RemFy<*)" j$)pFv9 9LZZUq#oN됼VPy0w"aO6[#}G%!{&t#[$q(판qZFq{PKvd$8OÓqIh 6=Fa7``5\a7+ͤDtK8e-!\Iظ[p*>h_"+i,wj-Вq?Zr/9nzqhn7yq)내g' nȾDl3ݎJQr )\pYb§.sY:vg(+7= T7шB z}? ťgږH}ƛ^B-o.p  ja>]2"kgu!°#qHx,[ZNQѝBAkًm'pܰ`?QJꯈg8ic9)h]T4M۾%9RIx:. c] 'Ғ&VIĴr ǦN梥 Jݭ2On4t&89hW.T3tӧKZ H&6 d<^W+ѕbx=6'+U r==j}L>KzΝej.$K:x$5Tr^rM,ǽe[Q戙y*:0rJGyy--٘O# Ld3֮rz";I$eC*2 T<۰rM.4J+FNL )Zطd"lh0s? .G˳R1HTO)<jJIJmSWK0]?=4%'t*nΫo\D\N0!cĎT[kgӕɅ"a =Ozф׼rr8]sJ4Y/rAn%s3R:;3vV$KUʜ`HTV9O[u)5 +A3A1*r:FPYВ:{֯VL"KIt}~ٴJk-Y†?׃c5!(.dfs?{.+[eO[9֪ƔߵVt[P;i ʝ@$c9nwWF\`ήLc/shSOD椾# >:iS mGlRfq) ̱ͥOpjvyT3g}k MMθǫ1m:NKX+<$8 泌o'=ծg>T#8R{G ZkZX8{`bhOxXbhS%ɿތ%y+G#۴qKngkǩkji]Z]c3l z\LJyh׈dhz5>GұIohpY)c8=W֬,n4u"ܲ sӵgi.S\lڑur8LUۡm<:ZB[{Ԛ9;t.nҴ9mVw I4(V$h|sN^b.6}ց QI B&@|>5^jɕ,4/2u*BFp˒6bNH${VSQZZaΗlpw$pƻiSI_9)ԋU\mTsrO@=kOxw4}:`ibEa;4烌!B/Xo /o# 0mak+jX23!ZE}>P?vXisGsV+K&Rf?)} sN嫹\ѕG_}K×3!3\*IcCzbui^qgxvwk!u>p'e>hSvI=G qiHۓJ3Vgvz2tZM7S=&tT)+>"Eny!ԕII\a7dsޚ<' ^!woR#sʷb ^1mu\Xo' gZRFQЅEe8 ǵJ-&H1ܱ8Y],B8OƟ7F)BQ$";qPsR$taE!`M;(3ب|e%Y8:{SV:r)) w:\jv1O(fo8\s@(a8jn+=e"XDЄ8'zd~UJsVoc*2w+yoAӣ~50 V!ch"q|okrDBo†8f5ddc&zqxo|Dfl܅a6dRS] DҠ*Bq|u= "TBd̉v1ۢ(mV,=p#zӒSHrևˋIn.mhX|ƛ}{$hn%gW6^}M[dQZnXjgm_/8//gů OmK\u؂g$`nZZ\Һ9nFr`HN~BRks:+EϷvRxnrr+>Uەt.j $+0>w9/B!-k"nYePL ] 9 jMFմdʔyd?pIZJ6<+xWdرgq ŦٕiNl^$ y<uVV1-BIYb0iΦxrEY/E$-ZsHȡT+;IX;)[ϴwI C9P1u v5*vBiy!KDM[$og_|::=Ib>\1da@ڼz6SEJ15ԝͨ_QXyDa̪FvsgirɧK1UۧqNQzIIHЬF#khp) y#4{5gmVզ.HL~eL*u8f4(Olֆ # GA;ְHT-zF:މ(&6Y|`*A}jeb#' 5lʋ$K(9'9ɮ򝢬zΛsZ(Alqt,}1PU+Hr^h6FoeGZ$7&4 PDVG 6I9$gs=6>|s mBG,yj[-3`om]#f.ժJZt(|.ɵ]2KW8d"v}os[ ܒr<“&RvD:lS/0rm:/$?g/yYXo>b8fč$x*+(GtG};%]@6$Cc pq^hQ![s4LhwFWqzc]>sr3N5l9A%mnD m8;#>駈aea'Y;T/49IU)\C}yE#0[ɾRԹ]j2&s34;;rhi(Jm:K i"U4L#8yURQ[\TM8R*# }WgU&RwWsGN [JZ)n:Íb#|X|£\JSqCU$ߑcBWzVjV:M2RiYRC?71I_DsJjKSV5%q 0e{J]AGk-gmfZB $sa8?NF+iq^Xax!%6S6- #>VtЩ٦ Q O dSOXngiY겦\B/.^ uM#.2qi"4z$JhADIt,UI%A׵lfF wuiݘGg 9䃟5^Fw[$hw`ۄY 8TSRAr%iaJֳ9kj=7Q;$c)<*gz+6&E$S+y 6;?KS͚x+KU,}(B,?yzR8:fkZ}ޏ&T,Bڬpz 3{D]8Vi$jPbcWr qԩ$ΒH6pK=ib12gAXUԡ5<KXJ$ېJ'뎵I8'reD--<+I.Vowϋ1"\G,I\#ڲ!SZQ뵄;CQp1@}kIVYHQ⬑iZK\ȀLLN3#iB>Ԡ0fy`myu8HӔ4ԞhX Dtb1FpI?5IF+M0~{i:z1ZmqzݝAu7x'-I$>tBMk3ҥx+Y՟{c j!Kǥ*2wKJѧ1HUqh3SIծvlG4g iЃPm'J3ZWX̐@b8'65vDR%q2$8]eLtojvH^GmȎ",=Xzysjn#Il`Ҽ+ vPг$9m^UU];ZM:~H1HO~V0OMQn2kXݘڼ ^y"IWx|<$L kr[C`F2u:PJv1n<5]Hw60p7ǧa5脓VGMs'+@śޝ[lm۵_:(Ҋ`^mݼhppԞ0' MFmrY3k<(!t'yehi٨V^^M{-ԒJ6OQ5)LQ(7r}hIn.E8v$8i8xeWN _'{d\Ae 1ޑܞ-3q?}qXބ7%N·v-/ `38`֯c\'jQ{yDD]V_{H#jw56՞'B؜d3~ >2K;(mrd#`}zS$MIZ>3."Yk`RFU *^+YSMe9c)mh뎞RW-.Gwo"9d ?g˜Kk'bޙȖMD`0!F{g8>czr&n!"$!,4gINOAں0K}SsVN+:x,⍕̑I2+ۯ[*JV*qQ֋yܥֶVa(q W)n΅dщ'5gY%P;n/T¨8EkM+jD{GNu"z>HQuѳNť; \ #U5wm3{QO2mcHq2?cUX.F3 aҴPnsRХykMKs l4qF+&GwBQvG-mp ֻ}rT@cf!v$қKAy^<-Uye^wIgY&{mV5Znvhsoim+t#q1@1M]-Hn,ǺId̙= *Nڳv,X΁ gPr:c=8ϵLV\cᶷ]q+=.*cp)a!hzUH+#\닪i7|12ȌF:W3Q *;hmD œ2WIɩ6=oFT<1k>eO+4`FF1|Lo XJ;lBmNAt=9ijv ug[ ~R;5zO7w},FӜpBzխHՏ:5#wVDmx !;N8y%&gxI% :Js r![1Ӡռp{}_^kRjL6d{NnNGAyZ$ $9?1M6R)zU"y3Rbnl5<܎knWKyk!L9S$ԧ%/ZMK6fGv 9ޮklЗ[ &M ]&D|Ţ9궹MiExe{-ޯزAp8@@XsKiFrF[$qI&$L y8'ᰟ.Nky>|RJUޯ^=Bc; M-́%%0q rs#Cz[)JR~<ܣؤR4}SW:OhtGmYZle+/+!pG~+Mtzޡokasg+m]Fzq}kxȎJp{4?TtYn2Y#!9=2 s+KwyH:dmw'KfoގwӮhqkrROXzgyd#H<Җkfuxi"Kvrc{)mHtuw<^4ms(i.o-."N+85kUk~ 7E4W/ XMx5mbSɖh886:g6fK\= *~I/r>=ja6ۛint|!B.J2I2[?hqj Inkf $*>p<|Fށ 'I-oUL+)v zբZgw״5-L[e@?w@'oMʝٓn4w?f{\[=@cɬIHsJR^^؅d.=y]hs{C:eqd7,k:D+ңU=81F!v0pdbs9n]XR7v~Vx1m[iV^IMBI349wY[ޖTJz+EՒ)C.1d9j>Uqs9|.Ltw*XF_8R3{tOjfZ4d]YX%ݒAN:GC[Ѧe) G" ;GRќo46s w1ZC,_)Ҿ 9ZZ"8Zmal/&fO>@=YɾSsg BۑOVXxr;: 3$;&i '批9=#g)\ L\F.&(mnf85RNڅkP-psI!7%xEB$sFWcurd_dIoK0P~vWV:X|s`fP1>@s>(} %ѕQsqjq6;")@cĊCF O[ݷ9a} Nw[ih}7fYUvۧ9Vн5z+Tq#[(Kp:|*0&Btլ\d9?JIfn|W5\eʦ z ٢1kY6WЭswQ ?dقBGnV؛JZ'f[ZGYPyCz "h V[3Fн}=O8K^kxU䑮o*:N{EddmNɚ*)-\rݭK,L5@H< sϵ5kYFmj|9V6$Er^d6sQE>%Ζ1 r[ cMe(rŷ+iitR]v8l[EΗ3_6FsJJ79t:3ãYӣmR) 1AO"-Z_tk]@̨68+9QAXT0uVYwV$6}s+6ܷ. S܋)(G~`N %MiW7Xd`2O?c*krծvtI`p hKʴqV3u4 wmFT0Eq-bՕď5In.$OP fV#l_K钽e-&B@1p'Moms"S߄՟iNs`qNLX\"b9UwdCtzwh-iU]iNj0gBUFK3SV6o-&YZxb(̸B[Lw& .')_[1YϪ$ywn# 8Ee8李22I4YjW0C0(~;8sݥR^Ο^!C6X#b)#n[<һ66RZca7ueڧ8zc9rZNRYg?o*^fF#$~mJqԖSi6˙!u*ԁm Dj+$X?t$7'`4%W$qִsK%I#G'VIc9;Ԁ9{(m5p\]85{0 "CIE;KCJKC4>ntnsW;-R:)IL-T c?ARՎU#{&XW|hXwMkA'E$(-..@" 8֢ьOc}y{yg#~ԩURH&!.-ED(F{W=Wʜ;`”@R>5̹R+iW7bXDv7e>i;NE;m?ė{ahOȪ7"PgXَR巼Eא.wHhTc+'gbD[N2]$c4%+\΢j6-k " XY3m1$=;W:'#5/$ֺ} }4Gt[U\<{TST#9S%syrZhRG$l-OL'9JWZFVOCH.Ep9 chwi:\MΦkzeİE 5yUsѼXzSJ}o&d x YjrJ|nnjiKmk,ʎb3 uѸH0GU]SOb`FsACG4{x"`% 6`7#).^i{|% +``qӞkXn3|+1\{ {˞쭱mkm vizVm"3yg9ӥVtQi1Pm(QX?^:Vr\˱էk,ىu[KU6iLc'{ ҝ.ufCm?2NJ;X^a@$.|#޼2 pqj n,<"$۵LSE.uī<<; g=uq߰S[ER uPKH s t&:oޱfiQ{m,V\p1>-_kUFV ٌJ $OFsRO/n HD1GZʜb u%}2B` 3*$dq;$ms^;J-l]_IzsW5U0z׏$6 ?(^$oCaZZ3CBfzQpÁ֮kXΝ>vP)Ȗ&,}ֱխ5o/o<7g'yT摣ԧcoo@V0}x)oI5'D 8hA$㸬dy/_%k rF-"8V١Ǚh"Woe+9#,y$g,Ӓ1إ5ly/cM4w[kE7LrHYrٻTڕm# S~ӻHbsix sMUk{3Shf5JQٜY[ݲV/^z͸ͽ3wKVFfdc3sN(lhhPY$K~ٮTZhrΛN8؅tllJ $FO88Ϧ8y޹I5LZɞ4,J33zf8RDPIn,W$m -E cn%ӬS{nۨ0Kl0x{δ^7P[|݀3:~+ۮ?oD-uIrs xQӮOkͤYĶ1S%_5!ECM’2en {:[$]æ$1I,n6y<(>)CEF<{"68%sYIUE֭mvӯ:[TMh8w~Ri)I茭#ᖢ_[6@ڕ!enIF>pELe lfr3Q!K-Pdk&$dtH¥>w~*oX'$QaZ M6ϴn m*=ORj+ϗU+UX'w` `z=e D}44oVvJ#  8泺2{C6{dVfn۟Qw]?v;(i{Gh`h q=c4z-A% Xl28'֒K4BR\Lw"3boa}rkR[hrklsq}jTBAbQjDk=Q-Nvqkt| ߟh8QTZ28[B}5 0U u*QmYrڵԵ'@Pj˝]fhhh ݌qֲCIJySJZ7);6p{ummeMnO'  'zuV-+[Cov2 "GrYWslB֖:Gs"8.S gPsPe#@qR6hW[xbLۙ=@ xzPvj7%KhKCbkt(9瞇މE/6܆wJf"PՈ#v-7-&T]fGJyb@ hzR~T3+%}IM-ZTbN50nIRq֚I%9^g3}٘i.$!c\/[Gg? ==zqN6<7;oPXm0WqBfh3FH՛i=3=GSv%(Q {d|W9 jQ0|:˽U7'UF ϧAI&2k{h(C/*Op{v]gʿi`O8ثYhLћo6YV&3#a7ڋjJjZk bd;'(yj(+lxSP#kX.r#2HA;x 4*]6yBSwuqf'[SwdvwM#ĬwuTbgkvCcg15Au,pǜ=MuԳ>Y[ms@XFy1IEs]n5YZ {h亮o7!&Oʛv[ Z6*e\`p3I4t!1?#Rv;ä;d/f#wms/]Zw7b@ {J>J;ic=qY+6cJSHb1GN9ȮV7w1&܀5GQԦjrRYi3#2\` ԍӓmaauya<MS+ v۳KIٜ[Uge x(U_ra>]ʼnohy$̱Ź~yVqSKbs=F.s}N$CFHYd$g sITjqS<*.E>jQGصFnKy;J'@kE{)ma;>3#Tq E&}3TYYEyټQWf7} jK^TVxݮ\94ҲХ(BH淴k%_3b<pw{zEڬu%vg֞ ֲD q-z W#gR0bX/Ŭ2F'spܲ.!AGj[qOr~Kǟƨ]MRB279wXX+ƬwR[?!^U0)apBe!7n:ӭ Ṷ[iD?+dԃ׽+v"QvвZKm$kc*ǧ| 2t9VZ}AG d'^<֪WOMvW:[X0 'nI>Xˡu"Pk0M @S?0>(F5¢Oi$78=;|Ʊ'wo< A7ӡKХxC}gy(Cȸh<{VsQ6:΋&},6񫽼 W;duaMӕ:Vz&]nVKS#*ّ0ܑϧjGZRΒH%GH. {gs֒j+ڶ>:Fn<ʇ~jztm7U>,o?퍺FS<.6]M+9cO OLk˄[g6!]8cĮqVj:֥ݔ.elI #o^浌=w&PV:僚m_+1%~^p1X*^:qi躬2Z$:kg#$ckuqu-['m&v'9oGJT3&ns)tv1םikGV-;]Iu2OOO6fPZ36S<Mݚ~Ďɸ̈F.rCZAW)^12# \g N)lʷBba2d^THCtm;#899y]VFY^+ k'R1RDN- fŠTKm]oTmJ(k6}*V̼/?4+&rSZBR9* 3jI}=qYSzzԳf/GVY){xІ'I2Ŵ ⟻k2"R$b5,7H0@5)4rQBÚ<7C(i#|ezW udsqxY[BrBNq#zէ)5}JfM݃qo(9MI'?E*®ui{EUK5d~#%A]**[w9GKIwq-6Cl #1HzGEEv#Lcc ; ]^oa,-7eʴs0{X//UIg Ƹ8۞>)Jt%t*="1jop"p~}Olсq5j*J [,L`|s1޶^3Jh$Q֞K9R(&6L[<s{hE_x)^vռݘ3dap{{^Sz#V0/\L9+בR(!^sssrf\`X?{-{ctƱwGy2Xus<mjgg%I$z8l[!uU?83A\Ng1qoFVcʐddh1v;{g1[V-,.q?SmƆp˞L:cSӭ,G,<fTRW%[>Zt+4'bٗ;Dz*Gbvqעl#3$m-JIJO].["5Yc<.#iSGa[IIy} ipgČ9Fl˦FJyQwjjhkm/.=dJn8]rs^ʦX*$K60psӓ3\Ҏs灮ZvӖH! Ͽҹ5{=]TqP!H+Dpqנi=M- BU[?)RAys\Ѭk^{Fu;XU{?8-¥ҹaM#W{ymRKA:aO9\}u嬶Ǜ:I99ϧjQC Iw1h`M8C*d-3).21S#tc(s-'oZI@yܣd 3"};ӱOZuHԨ~ ^ xd+ehQb%@ƒZJ63|'?idc~ ߌ0G {ɗu}m|<6iH*p 3I9}*l[-AWeM}S@qENѸ|Rz 4}Z%;"W L㏩U|(rp⛁sNiyIh%%mYkWӆom$kG,69P`A}JY2[F,j$vr'qc?Β݄긢!ad#$?v9VN^ѯ$U|%}nk37nC 8 >ink4gi:G)9@PLRLѦku17 Z46;kU,# 2M_ވUwAxe!*3zu=bU;%S_ŵ6xcvkF9Ǩ{aJ/E*| ӊַ.;*d*wd8<1R+]|kZ>[m gc%=8V`X]]^byH`_Б|S)Lߙs"͵Ɵ[>8eP#z1TF%}dgRڼ䞘.i!Bffѝhx9֞NV3{"ḪHgpN7dȮỉs":ūi6c=4 qɜqQV>ʹ5OC:ޤ R"{ nsۀLgQ8zTubA lPEr;nWzZv, I3beШr՝jxfGoK̎BŜ2^:92˞15=iMnʹۨnnRՔCJ Y/{]#l4+I+Q,8GcNW6Y}9n\Xj4/4E9ic5@lc?,l]O2$%DZIdv*椪{ Ay#X'h&ҧЎ֯쌓Z Mґܙ{hW U燬mZ[K)rgQ3t3M8HkhZ#&F{<,QGV=:0I7i6NlRf zVMK$n.MNn`1PJH}I)ӋѢg9~SG!dF89<J*2v/RWYuKh'b[6 v\ӎD:_+ZeM5Dm ; q8iSwI._y]5xڭޓR`I~ y8UFZ3-b!M;Kf[ 9"A/\:u9'-7+l˒ŬO ۥfHr8NAxT`/:ks$ET?.a`Q9je"e[f}?^IcE9\8&VәlwW3h|I`r9U/ٮTJV8 :/a n$,,IϰsBqR47f,.yM䪉~I8 }Vʴr0ג}Wٲn2g m3>Q1Ì*GdcT9TYnT[Ki-2 6C9G{Kh1IZ.O.$bҨw9ҔZZOӵOR'ab,X#b/r=j/ $E:!,BFQA-Lpl9r茟薑hhqmʸd=~28>[S}V K/4 Vw9H9pFy>E);jsBlkiTydW#˴ڌp2}}(M^gʕPk/dFR#ֵ';ZB* ɥam<hM`$g 5}U&W\7%Ĥ{@ sznT4PP0D鴖9Rp~WFTok :t҈꧃q]<=I'$u (LidYn`*ˎr@9Qu= ]i\\-͍> l;9orY}+u{Xݗݞz*,xQ]raz䎟ZR;j+[vn, 0%O|NMhs8jk}$ n'V|眿R us= &kjq͑[?ܘNXE:]R5nt.bv(99YYZYlz41O쉣ʔ'vr8,4^60fc Cy:u41?V=ո@ؔfٮS{[u9sRb^mse:Y4Jtn.A%F@T o5=LK.PyeV̀OL~+h5k3 )g\8Dcd`pr "y~#0[Ǯ%ȼic`J}Ws֔ U'-DF)#P?=~eE&6RuNHjDrNVkfmn٭m7 in; x϶y},\('.ͳdيѣ0 Kcڪ*-_G:"Z+ # *nŭQt>8eGZvI-̠7''$zlW%jWH9$$t?Zu(:z2ӌ}iooj-[C"<׽DiCw_e4翰,pJ 펇^^Í Թkf}̑O GNt%9湢 V{IQf*z!} QVPۂШFQB'Nn dq׽J*ȉ҃ՓǫO.T32Xce>~kem{a,,%QUOc]kw<bwEp8_|VV5"ֻϩyvaFFL X&rA+(㴚=y6ӐI95\YȦԉ|j"k0B-0z>t8d#B͑j~ Z &"1e@G*h󞟈E-9}ΏCZG,ym+19FQvHϛEǾ8Seb90+IM7u9_Q͸v'hسrZ2N ~3ލzV*kgB\[EK@6[d€>^TQ8U:q۹tb[uos!ufd0$syOƼRNzkBY.䶱j/_ T<炄]3兩XcG..p69+]|κuw§xzSN_"pn:xBu; j^|B+i#q9'  sN i1ya _Z\3WJi;疆kf4P#`7[8%5OcJHImnK5FWRw/q-4|qw kn$@F.4F-$]CVׯun?G u';3q{YZ".1ӌzTJ:n$:bH`i>ܓXVB$!`$/b3kPw`? :'fK{9`NNH9#WM=qWޑZmV -o=lMq iY_M9[Yn3W_qbd-2+mp;`4`is#Q`٢"߾ecxinͽZN6d{0<j8Oأ1e'뚣A+vb R5Уn&)sۥla:Qz؛NM[l <9s)GXn azJs5-f qփnb{yĝWd8lCښЗi-E{뗞䑺i.MLʻ b@/*F\npF 9Ȧ5boɵA ull,H\dvaI.H /RjZєVy R O|tTML4WlTfP?1xVk, Gn i$@q隹ˡ[|Ks_$ǥǼ*#pN:`Fv:JC+Q :< ;YwzcxoeotUfZi:l>u-$hX>Q\ӍGU)>347]vq|$;q`t>/;]?p޳ܦ| =rqnNq <wI-ym&(Jz}9g:wf;XkYO4@854ИquQڬMw44po`U$?/n_h(恣p\cζC$rH}M.Q_[4.c,'q);~J 7m]\D6gUAN.=NG;\M"2ẏn)C}YWIwEap>XÜgϪ4[K{el&؃ Ix{;#Sy,ƞ^A:HLlO+w4S}z_ZxEbif6>frQ1<8AjeѨ.:~e +t  @ݟlq э=bCCrdKZh ʷx#Y''yhljXB< cWRuhgNM=}:+$m?oVjxF6ϹG#n}GrqGD^Ѿ Vl< fWǎOAVT8Zvh[Ό𬥙-R(dcvzqF3߾uE3Z7(}@,gpzqRpPj<0Lq{qovH3ɜ O1g7_)g%̰@ىfsԜ Ւ̫\ԽqME!|n$>Ҫ% 롕*ԽxsPN]ʘg7#'Ҳq"O_Х{L~-[|, qu @#fOF\W7à_o4.=Aebg Hc; #kR)Fw"5*+"_,Iqkwp%$^@ rA5t){ZuUdkrMXglT|ud=pu(zA&`PI׿j|t-ٲmyFhco_J3庺Ff& 6%m8j9FVKiܰXLQGqEeyd(Jl6j\:>P_ ^N9826&RMjh:šPIѼIN8zWg<-'#8S=i(Þz[5CO3_O4 : 19ܿ/㞄}*` %mqp1f}p>\tS5.>lZ֟ijr<- ?xu.6H;EŲ nrftL؃ۻ.%$x($JR~Gj%B&p=:\tM6Zs5ӅH8~8{U$e)>?n˛L 6%SsIl<$l"LMD\7Ǻ5G9=:ROC:d-RM_+%#rY*hMRדa\EO_ZNь]V޳%Ժ]ȧ=p'_Z"eN6Zƨ(ڱDD CF hEm1{L:m, 95*]b؂R(ZͤջN>AZt%O[{k>9du㆓<ЌmFhN$A󼌍R*jEs|$ݡӮܜ,وPմ R lQ!(MY+H .xh6%`ViXxcKskٔpX8u$TM8uDmԷwE<(YJ=>_$S4i* nob9!"=8+HҜU d_\WV]SImfʨ$:RJ*ZNXe"7g${PzOih0{yA3"rzvWu2I Mn˴q9s9iKBH4(dffh2r1wuϵ)>]%Wwwf2݆A^4ޢņ|]-y/Gv\!ꬽ?B0Ûr2v?mVXMpH WE.Okv *N26$wr|K˩[}YJdG"t9UcuJv$lgQPc!Iy't(9ts'[;j in|˳c=T֧41 /'=C[Xt%H\RF =(GE;TOK/qi#n SdvNF2XxsWѵ >MmmI6'Sh(O㉉V o&Xv 5|kV3x z bBp9 zp"]6x.1jASK^Miuim]-y,.H pcUqLRM= jJN9Rxt?CVv;/#zJHu17JAvGzt(=uFaN5=I #ǿh&I#3URM _Z8o =OL8Cږ䌤b] uS)Es4E|֩ח\IK&sA*KCX<9xV,IfF*p3]40򨜞F5 4jesfC4@naqtM=Fˉ-搾+ǿsmƴZvmiWrz++XTc'v>.C#.w.'N=Q}E m\X*sqlQWzrh-!kR4 t?5j܉֜,vK;{Koi\ +|)8neihK7u9ߢ3* ')sҵ)B$OD!p:^sA|DԬcn(mNgY %f6R 0Ϯ)*EhLe'9/Z1[,b#<*][2 *oޅK{6Y}SBKfR`0'1t9f"Z~:%?vZz=74z\-{|޽%aa(a#$k[O񍳽VSleKJ3|qQۿFэ:NIE/Nv8.o| 21\Nlҋކ|9oʒXХa ($q22xؗ7+7tejVit-O3yBX2{No]Ӵ;=G4V$YT8z㊨?..A,c od}vFRd9TS^xEp<'_P= :Q2z6985.Qm28{xa=s- 9hQSg #G\Ivhѳ5IctóRvs;#.X^vkgYu8;/!QX+Drz]2 -mKr>rו%m:bռ3 ;`W G.+v{s/p5b9wǶj,LQ5j([yP zǭg)It#wūO <# 89WfzjvkjZ钩{q("ۃ=ދ"f0m5ǓRIt`n~_qd M$IKsx$qMe=9Qxqs5դ) Q[Đ*Jv<9UW5Gef Wd^FɃ勰*]lڛкXIoƸXٻh8Sϯ:t7fvjJFv L\Py_tqF?Im1*b Ƌ!RK3NPX#e 0^W,m ܟÊ+#xy'x~p՘6}=+5uUEF !O8y9(]J/|'nZƌ V%I#'Xd]'-9lzA v訽v; h![lu&8ճfSr7NbO|Dad&:# g'F;Ƥ!u4}#Sנa%LSBqsq\rlz. mͺ, Qp\TMIIo.+IYwۀ8+Q4ӻEGcã\--TGij(#~6s(_Fgk!6MҴqX"cKmN%J,*DNgkgD'xXiGRChM.Jʭ1[$-̋/kh&mF+idFc?S]kEcxsA ukbg3) Gq<`f E#ԠwPC*V@Hl$ǵ7 [2(4[:ڈq!(Tl)MY_J4ۻfB)?iXtBmgi7bKt[x[m wjH!rZ֣ȭ7[n]Bf~f扺Yh_Ok:mK+|y:s)F1} YI5vf^x{UQK(Ͼ*\,.=.C˜}ӐO W"XD(-Q81L<+;~_BD]|6$gSQo/4bK p. aWM6cRw,jz=Ƨ5|qwv'po񨻧-שxb[] +[?5eU}dᛐz2:)MhC5\#> .Ү a;gȅ@<pQUJ{=R>L=Ϋ u:+kvt:-&H/.w8*{\.I;;y&Q,6q"fNSWeh`ɥ\J-3 IF/(&yU \˩} &&GpA{،fB+hxJϔ1bV˱vG5Nm>Q.K<`Q>=.ek\%(FWfumH/>rrPe }]Фyw9I7̑Zl\3dٛbA`:*'J4,wkNLG&GNoUFmsҹEuZEJbkvquys2,N7b6j1ԣ&immAwwsZAtqs,@ry涆.b7lj asoesa nl8ǽ)JclGzJ._vYG,lsrT΅%lK,yU@Oz{esX-D]pRj&g~iB.% (#}Ӓ9I]-t ;Zg[\&U2%#nNJ $fts-Wt~阦rO#$W>!^Λpm&%ɳKc7L wn'NG~:^p,TFĈG9 zd~WKIuȟR%!snz185){OE?Ci{+;i|rZH6H9'n0=^*j6#k,k66 S<DI+ʓy'Zwi[V̚]ْf%䝟lj\efM^sF5% BT*T8 8'snwKFq:ll|Q߿͞+ 4tYG4-fSM&xq+m ͌`CX+]&ojVmG#GiR܍zG)wÕN{>2 w uũ-S] :xQ7#W`~N1J-=Qqz#k9Y$͘W )$jօӼBmŴII?A؋x 1<sy lsKQ.@IDH8>ʡv-ddPKVR fdY|9\`31U[q8smO?o/ej]s$U26- r j ` ϕVӼNv[x< #H ZTRD.ٌc{4=;1!o8 GAJ⓰'\ɖWpZm/ [U;VY31sޡZC{s Z$tϩURUcT)Y!y=i\?M''w-FF*M'1`]H`}( 7zgҖ}$fvN0S̓R) ݍSزԖr%e\p:L?*W rF_phOLT٠r%RʬF'nS\ՏTm5&! zGOX;-~c.a?f`Ÿ}xՑ,+X-==nVIe0Ĺ**@hyUSB=OE5Wp:%B>R>>WAjlnޤֲ[l^nZyQ\g⢥^mY*ntصqEdu}Qq Iq# S:^Htӛ@!X/>cUѝ Ş-c (9R>A3uk{Ƚ\]yba = lrwF0RY)%Ʋ@Xm'=^~ZFwl*WsuEX"v ^1zTF5쥇\en# T 2GR_~}wlQ}^q jm*k b0F>p '{ջn-E}c潖飙d) Eu%EK6H`aTۄC-IP]De9\8mK Ii=Tv߇t j6z*1>Y b N8pQR\_g:0mJRFceFyԄfuA/BG4WdbX\ZeX|NH FjU+GK5iˣ?im`y;T=S֌Gj:)Gr%%eR H55mu3K-L}SX}+fߌecI}TE=M2m4";Af¬#eM2168>828NtOEiͮ$H;Cn;z~<ZpS]WwMpvP<98ׯOR ˈ=W?,[JI!݈ ~_ֽ*<xgwVKXD7Eњ19 U#k& ?}Yh什Ժ\dhՆH?j.+Q甴R34ԙm%i!R.WAC)%Tʵ; [CtحlKbSJqTpUΔhrS|,l// hg`fLaHp\.Z7Skr6q7 E ,cs+cb4%퉧M[ GG"0={Z=7EA)V@3$q|NԻ?L}Nّ2',qHwRKa!/2KÑ3t蔠ihgw{& Fs$NsԚƢ)8Z3խOul-e7) [d;fJ2&5;BU S#$5N\§MEhe,r$R4%HKg! #k1a'0Fq@v HGWYkisO;iZ>1BmGo! v ڮ;29{|Ur:\׺a۶ =g؃4:NV>"WZN!%q${3J4ͫԴz!--('h9#dᦀ^V)=ֹ$j 5;::Q3\DTz$pm@s#j <#yW{JU]7xq5ݬWG*BF8[t1拂:fs$eDeI2I玝k#)bxWar`C%ւEҮt 33Ǖ'gvfrE=icJ7)F*4OE^hwZͭ]NmB X=1krN38_Kes)x]X y*ip~餤/yYPXB/W8ZJZ)E-ٳHdxK#{U!wӒvޑ{;3^3B(PB{Sv9EhaisEur#t[y2@)ҏ'r ^%,lak^#8 cecpvFN+;{b[>϶č cԞ3<zΒo;m5nu8,1yI#%⹱z=pa4u.?ż!]WRӣӧ-ȹxt{0S9~']:);$BGu$Ohh 6{qWq ?ެyeڶvQ 6=}+fԞkĢr&q:N):nΆO}4ס ]  ֽ5{wVWsruF4O2`(>cv1bҧk#A]=|0_MKLYqdjO LL);vͥ%NiYCq@ A E[F;T#:R==  )`q#\Mo(+XἏWZP4Pm HI*tmZj3\GeimjP1#03}qSex*WzM&MO%vt'`gtΣm{F+hz=87B6xr<5ع7X|U=ۺ6b@%b kp2ǏLV֚ϭ^ux5qmN-WBשZ _#)U]5o$J`ݳoVq*G3Aue 2̮ Ear;o{oCxtHiK"; ;SՆP.]:0{mԫLF)Vs^qm+Fj׷7ҠBYvH T/ZieƜk-ܠ!qr[9hlv~dVmiSnۿ'ÚMfBn8;*- NVZx/ϥ6/voP1 hQSwg5fAj$Eٝ)nu^L =(S391XhrJkMǑm .ΏzuZpè\#RJ:2VPk@M\tvegufa%֦4Ʋ`!s6y q>Ji,۶ 7;y0b*4'?9)-la`: GKG$7׊-oI%x/}rD}jqi#FH#ڊnVΚ?v ȓFYdO5jn;A64ĆpFSt+hԌ6G33&]X"XFnpI'=~cU Fivѻ`(o[UA< O! =y )M7"siە > Ekyv:dwq;6}+CjrIA$Zba=(a#?/M'[MX@nd$パ>OLUrujKRMmttxeҢHIʉ6"AO'J)Q/v^q麍Ȓ&d\ TPb˛9ΩKBc2œ1} ss\ũ˫I?2m[o gp%3*J^NDOi-0.6ea v+ց$s]Z[v9zrjǚЩcu-ޥsi!@#q#'Ozq+k\l3ͅ}7GJ;XZِMu4%6cndf0WlV'emKM{-/©-%a"'~Y'/&,"R3:qǭ-dΏgϤiʈYkuϧnIYli2_Xm{wl#,hA$/ʝCp"\dm㷨隵Js|<+ek=ĭP}Ѥ\2=;zJWi?^-ov|JJHgJ<-MkmնL3ZrT8#n=xmq3J] wWd@[$|mAt+EZ3VLÕAocVHY-u ˽L{c Ƿ% Y謈続=WmT(asO8Ucțkws%("6 Y'N5NcsR_˯[c1FqO+R[=%X#s,d0Xq3M[؝G'dt"UGry!wOrNHn-nsZΊ/g$lsAuf[#yc-H8)kcx~H?9%EܠcUʍHϚ(+i-.dI4ۘ!KCw,k>I )z.p{HV_[[(́[%[8Gc^̫RP|ڮ. G,rzzWzacVf!=`;rvOž4X,@P2郞LܥdK2z:f4Sn|敂K/.7Xb:֚Ĵs/F֥-3[ـw{#ҔioUU8{#mWTK%h.z+N2pH)4Fk| #Co,B>Bx8VPmw+t)LjGW3IəI- k֫;\btն9`VMǨN>Xr"?_қudi ;8kd@ F[n['¹셹=A#fׯ=sMP`(DO)FKzS%mحHw·F& Jr$vS"$zdSR/j:fkxMdC7NUJ] T`Ϭɩnf0T`1;j~fk )10#! 86>vKH Ѧdmv1%$ƨ(Iu9({ I&DN8R]U:[t[Q1-H7.d $ S̓WJY<[z,oţĶ%!oxcZN"F-^;{qsifo?=îd00##=6#Ң0oTsW+im-,/}RiZs#}ٮT)8^Dso($ku^ IM31z}*4wF=hvK2f*aUT*2@'9krC=.e̶rjfKIٸ c[9Ek^2) "09ڠ Iu\[~y;*+KFCy5bX}1?,5 Mr6 "ȐndFyah2ʤ=?OJFjF1 ;"K jɳKeA=UIP]rȫZ'yVgEw[6\u:bIQ]tމ#ϫ7SH&t_@մ+~8yɁS2AՎ.(ׅ8L{ki(5I$? T?o)Nwn72vX$qں}Z- -RyR/Y[)x:`]UucU!3+ĖQZkm*FW<\sPO^B@+[ss6TN.S66gmu$3l.&FZ-YCFV%ǔ sJOSiǙY&}i^f;XBϦNjd򃎪GmKX++tEM?0joFs-^u !Ȋs;B#~˗# ڷ;˩1p{nWHhaG0sQ5ccg*R\ڝ-n#MAl%}OVFYfF Ki0bTne;WW*-Sw_riy%F&iT \`穭!ZP2RV-SM*,ZNfLߡߜ&*{hYz u-Zc?\h*pHItRzi<Ծ]M̤+`+hզi{jCk%oQ5d²kvsS.rON*Rǫ<-'Kе'mn(Y:`wխ&)֖9bS5͸)E>\[4BQXˌA 䍮殕$׶ũ -.3*@){p[s'/wR ӝŘFHB\*W*pOj_6t pAکo%φdӭmQɒ[! 2zw~W]wOy;,rsTuF**Tchkyc!ۿB=jZ5r}C[5f-_"I tM[dJVȂ+xo-APayFE/j`4MoyI.N$?q֔9vHO`1gN};Yb"! 3)ĺ)<)SQH!1~j ሪC#F~aaSo2enHWwg5؉ :OM vGMo\kCK) NYu8?+USe:^^|FW#99=;WЧx;5USFM muHerb6g?"Z+5[ZnXm'KGO ~T15$ݬdG<:hkŝS'p? ]ﻕersJrr=2?rFsA)"ƨ++0(KS?v(<|;b+Z}ʊD/띧N:wcCB[Gxd%j\8#5'M9m{ a/.ِv;II*oE'|6!n@˴qo֔iS)nxV\O^i$q+'c=W[;# ѢI-CQ}z¡N|殺$&h%Q1,>T xRYAE# A'wm9< 0AsP{Teub+;/BRbv={qGȩQ՚WϣXEo(7 X3I#=~4lsjԃO;x'f*Tg^cJM\Adbu z#QќӇwٛڵQ_Kp,BA2Hr2qښOcRGs͵.mR(F9hLpvy$ԭKcpWbUP$ XE{/Nm1`G B7M2C*8<;%Sk54!ld%l sϥS('QlCirv q&C(lr)r5UKc#k)fG 7oFk wZM$BQ߃[T:F0/ǥ }f`0, M.W5}NZʕ~GC'$Nn|9DN8$'cA=@SJM]tsM9h3':M4o ҰO+Ocu-OJ[JX,;Â0{(sAisZůduX;pG1ZL<{}I18`x=x9Mv:(M[Iy_3+MM}66殩4e0Py8݀sU{ys٭ #"9R[%#߂3Lr]yܮT0XꞠL܈҂wJpp(2:*V4;Q3 /9lLKܽXA U6Hǚ/Q+>kʌ-E$b@Xn[HnpbyϞӭ"$WEȭdgY ݾ;msdx> lc_/$g t5 O/މ7|b<-յKK7ea +VUI7Ixs",'Bgj2QѼf91>k'I\?X鱶ǥ3T?YƌZƐNȌ/_; u`hmBwn79Wnt׃ltjԩ葖U>٧reE`2v08kTrwfVק2\.y8xU:1<ɒhͤWS)GRKmpp[=8j=^tgʽ,ͥj$ugpEDiWHFuZIt D`wPqӽuSziJ_ٯn'UvBp YJ>Jvff.F}*NXWbE ~F$hM̿8#<ۨ=ƥ7>gGپ )u&Ua08#Үlqr4$싉"km:lHzc>iZ%[_H@P(Fg.=?^VkMSHj0H",n?iJ3Ovj4\b["{i .PȦ yWUl$EJb)SmY5Qp6[95Zj4m^y 9pJΧ'n4Gݗv6}=oI7I5$xhY@%dRsvNg*zouxK]k)6Ԣ͵pA;ךF*cNhM;#ӭ%hʳn8/ZB.zz%%kv67Rys$H lM1S؃VBFAu IW" dx:4 ba&%:;bY\Ndw2sU,U1;_{1oSiSG  9gWڧ:+v̛ǧQBz~Mv"ڟ`I\:nPA#JOtm%V97yۈ`Wc(Ãѣ2sŖW>"C$qm#fVYPzzg.tݶREkpi 2r`t[yj;+,:>jؓsrrpqߐ=s?Nm$113 ' # qOzqr.ұxPCoa)Ab8aqvR7 -[1;Y<$J`t }8gW*҄=NnV Vkr7Č6M*I$~ a JJw`q$ zV?u|ՓoZYdwU6p'!\09:uHSh_7[OJUx{{&.0eRIힼPV"mJt7X# \-6dy=qwq++ jߓ3sBNWKFd]%ԣL7IfrArpF@bB-u-cmulVD,H=sp}j͗u~txÓ,[τku$tnyC~f:֡Co,0HQRCZi\Omm,0"8v8\z vEOu5!r={IYIlji5Ag20^ASm+#wMzv 4d6`qn:`QZ34=s&FO$?*>J8}+t&d(KFa\ZNL!+%婹fP"m1|>ūXً >nG?N=m;a"6:JMngg|j']Oh.{dGU$<^KMw;8$@#r#S9MtUVє[],m5 ^rٗrNŴMaFW Fr~ƜܖV|G 4ptA['>̴F3՝WQmYk3 H$] _I3;@fn Vm:𼑹1l?Gr;ߺʃ'#l+Şkx.38?*s^R3(13kPF@'Rz5Қ9&ܞ\ZRM{hUw$9+Y98iֈ..mRR1՘$,|7kYDM4"+#t2 q~Mg^W$ɧmu {$lxIБ|JofmfѡWLCd8=2A]IirbW\i# Lag^õAj^֤Sv1F)ZxĿoĚ}i%>eT뀤=?:eJ2ݻ:QjPYKTe,d V#s麲T:e'+m]ɦErZfy9^3j+I4_hQia!")8lc4EԢ%cJ-~ɯ.e4ytrf#\ޟMjDYZUK۹HCr@x4yZ:3U5H忷/Y3@#NxwHK\@4}ARH!NOG1&Sҵ(E\Ɓ{F0@m[DĮV5m{-BRwq#?UmAG}q&<ɧ-a"clҽ[V_LB[} QN'g:nVijex͂gk4{78*1ϸqtϱDR+8%hζa}pz.Mf=͹6VuݳoQGp#ǁ8f#;7𕭙b7axui',OL}?J,ig;+uݝHBO*mNĶW;A$7$w3gHǰIW9˽feԲ#d {UF\$-\~ c#­xSz-7J7%U0vc<8gg*Zmclo|=D[cɭ=v9NQA2E: !N?\V5b*^C3qrB81+H%]:ΌA#DfT[h/Lx:/Ӟ\ݵ3ѭ5K5 odIL=l [ܚ].$**Cq;_ eVkCjob ;Zrc0B 2'S.%+hz{]q{ShM*s7Zqv[Z9W$;Ý'<ԣޓ}:#uf%yLA90֟0SoSzneN#g*H9ۊGs9jFÚͅ䈒nN39(4s֦,3Ɯa-K7{`& t2/[Ooq#(C?^خeKn`Tz#.n$8`W2WU5$G~N;sҥ)jQm [dDr59.,S!vQ.T 'xs$vb\0G\S<:JM7--`ynH?s;HFMK)O}#ұPNIXQ ynm((Ĝ`>yں33co.693Z(3fq~"]o("Ea y Px=Rn֮}^B Q,(Y^J1*Btlu^g47tTkCVWDjKy1>t@5O^՝كFml63:& c_Sۿ5FyxwBR#ӮڷVb[#ƪ&DrxOOuhn<)pyGwK(^Aʎ34]EVT^Ҥ.DBb\tOJFKԇNAl.:Epp:;w[ibHPS/.vKp> U;bFwgzjZ^J0.w/Q==tƽIEEzѥg%f%+(ܫ0ң6~\p:VpiXέ9_3i$,[ɬ$sT'.ZC'1R@@* 8ҾKu x˱Lv2\G9El͗3^yIV/(;,czg+z흦}h($hRq^Mc*onHb]BUXd"VCԝ<֛ZkvRZ\G#jvW)o&7Jx黜jg.K.n{jUu)giimrtLjyp ^k TZ YtMx2(Iw kNd/vQA @Lӟ81!׃OWFVްԭZ8t;kuRf1^䃜{qMԾߙ.vJi/l漽3F^< 03G=*y/9XKi#6`g==XX}DOkkЉBK:px_CUǬf++s C*# sh #bBp+ܨYUQZhh %#A8&~H?!volJ^k#jX'VmL\GC- U=b-$稤݊R0HR\g?A)yć Ǹ=) 6v@`hdB(pijKj̰ڈK40#'#ЈTլHV B6ow0 oک3cd^# KIl?K3ji[y-! ϰֱJÞ-rAxZ=^i@Q( faJMO1;IyrzЖ)գyp#o^Ү)-N~w5v3:{$;%M'8 ɑ2z):"cQ^ב?/bfR(F `D*0y&I R5mʬvizuڭu+8j%&D控XG .bi$sSGŵ4nluKudi\ÌY6r VϬC 0cHyS5fb^Lڻۭ=HU9sJX_Q?w#ֳiZu_ /R዇\Wt8ZKFJ)-CM8<C'pFE8QJ5=-,t$1YU~cϿN.`o6酳ۈ91\UMKYk{!}&)X1]Auvgf]D,|8K|t3]teRVrxTiڤV\K3HHJ1*RVF<.Jy̛,`c?Oeb5~ՕװMsZ> Tp eS\.$t_ ڷ֎\#mXb}95>^H۳N(o{f~ A$?J&:&>_j&o%ޤc~rUEM٭Hnм9k.rX3S:~iUݩRZe֓R2>8Ң{(9rD=y,;yRR3@V֤էxCE"Z8Ri# X8GsZw$z3^=ĎHN(@8 +&T<meIye[oRq3sKfoqkibюI6F9OctI)FN|;yeA%w\S3JKb[z]GSX̫aa3rrvRq45jj֖VgoaG8igqa,p2:JluF  ڕ#Q<=>{UFPoXՕڻ4~BsqIfMgmfĚ80+հHEM[KjDL!g:GM/PxV+4'Ό?8k9kըӹ\i)V3˱B9C\S3璆+ {#qD qrmZi7Q!?(o~XWFj..q$rǡ|e $c1=e;X<:F_Zgݗ6gQvҫ˚TYYH <ۊ I ,kz L4l+-WkQ >JweKqfͪ qpl|c<÷sֺa]J~l*3nYİ-ڑK88W+Qi9ś\ǡ 8<_g%  HW4щj)(Yڣd8hq\Kkڭ2iɸ%}Ќ?(hsZ;teAh4,zUMt1NyyGEƞƻ1*NVZxHZo=Pk¶ PpzsW(8"9ntH!Q`,!%H>9C]6b΍l[49{T.;3<*i잁)(+Ү.t-N ჰԎGKq9-ͯmld3 Section: [ APP0] Size: 14 Section: [ DQT] Size: 130 Section: [ SOF0] Size: 15 Section: [ DHT] Size: 416 Section: [ SOS] Size: 10 Image data size: 135204 pexif-0.15/test/data/rose.jpg000066400000000000000000000247331252620274100161260ustar00rootroot00000000000000JFIFHH+ExifII* z(2i CanonCanon DIGITAL IXUS II2006:01:14 15:35:544<0220DXl t|   |N0100*P 2006:01:14 15:35:542006:01:14 15:35:54 k .J"F Nn~  # "\@Z jD  q*rrr000IMG:DIGITAL IXUS II JPEGFirmware Version 2.01D @_ @pXJFIFHH AppleMark   !'")(&"&%+0=4+-:.%&5I6:?AEEE*3KQKCP=CEB   B,&,BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz" ?)`nIH ږIGRWn88H W qSLC8S%69"yW+{G#F$ d0@=;P"VJPR0Pp:Pl<뎄~"- tԩ $q zy)@Fr=*в+~axvwb5ke< e'K(L6S2 m" (?#X$3ԂWt-Tdp=M3O,\ۚKE@1֬lg1:WEH-~FkIaPù@N7w[@@^ c#gH 洮STQz RFqެ:#IK@pg RZKo$::U#T{T0JLL{$n01R3c"*ew|Gn)hbq4W#(M:0 I#uUF5/ʄǥtqhv¬ݱޒ:FY}1XNzPœv3Ř^ی 'ʂ{cI'ڲRos>cwնwNYbsWǝiǎǑQ)a&EoJt:hg8MI`A꣊Њ , jrr 'ޫ~ky- c"˸ b%_OC[Rpѩje%#$)]}wGcL\:5ynF$Rs'GHԌ0($s$9cjvN™ RuKI'El`{)3U9vHi.-j~I@#5jm;3i< mܹEmnecӡO[eutג:nBxll\aQZO,x=Y7)2NNCrMc>gNt?ұcǕHxá\ͻN_ 329Ĉ1R63Si!l$[G+tjӗ+"E9\0>'HHWfw3gc>wU4̸su_>:c]l^ѐzPTlpqQD&C`[|`qSSKHrsڕSbZ,Z'gjӂ3Fpgů84{/0A5' '8<8j\~V2ӡitU#; Ls$is öP0kw-JaFOBS>Pz}*m5sssr2FOSrmjt gB7)T]F7nf%EY \ =7oXϠjy`(r2F/ k{ؕ2 kOm"iFJGzʥ[=CoBsW+ R@{Vʤ^nMj3%8J[V<נUf-y$N\Dgީ Td`q]C+!qJ@QךgHb][zpeZV+J C|۹9JCBajԓ"đ/U()/9*MĜ۲ dǥr!V#,&rN r֍o UԣntbmϽOPp{ F+OF -#+Ζ)8Ţ`ܬq (=k$Ofy@ljܹYe #+yJ*NG6#XF;Ee X֭E;U9nUf2]GEo4":;SهXʰP MXv ϛ[-clisQEگD>r DqwOLn|u+H\uS̍BT 6OV5n~Eqfꘌ)pjj8?z훖q[ԃ˧Xw=\јA3^5j6@ҳ|ep9+ɹA5]z1'MNALUБF: 7Ef_j OP?1ٍ+O~?Lp=^Ƥ\7=3:x dRAWK>ψK oA zƪ|Q4rc5I\k /:ȇzWXVz6"vN=Jo.`J5=w(%SoB"ޫ8?q?3BMj[8u7uұL2EvRUF1VWC6*rc~N6S%:wLTHs޴YS3 +,Krh' mx 泮.^6g{gFÎx5!ɫܙKzndHf8Yo4v5>,Ri0̄)<p3)%HEq Va?Z~wv4Q~畷%U ɥ zPf.G784ր$Y 6`+ɤ? 'Wҁ0GJDRizFhnC   ")"1+320+/.6.Ne.#=1@VNGhs ʪ1gnZLmB;7=s((^QF7ϳB`ڦݫ &^Eſ%HqpdkZ㶻|cF-PCD$s:3hj*^>bN2bjW\vKW U霛礴G@- J}kNke@^'#漝)WP^e`gWKw"D(`j,ـ)^VF˫Dq ֙2M]DyjF% !12"A#0BeWcQf҈KV|gmk)/Ľ4zQBMJ.$KzV+ -$('CgboBqd"Qz(iJ7Dqef!QD"/h\jHDepp,񷽽8H&1HcX"DKq.*pRTGH|D~DXj]%Nt}`]7Dat%nʤGus鏲xH_(2.Gr} 7I~;%CQT##SΈF2L"Rl%U'G2s.xRt|jZCUǩDwõ5#㸽$]<8RNF˧8,n#m≪hUEYh9±Wΰs=<×Ybe6)2$5 /n7w'^?!! 10A"?'1͎*CUQVł""(P.tO1+ X>{tQ>'ݬ v<|2*NdBCCi/\fn 01A!Q?ȱ.!ȱMd~!c-"!1AQaq ?!XQ$!+< :4NK|1?64 %% %* F޿~ƧdmX{kC E&ۅmׅy bC7W @{ &( N7C] :գ_]eпٹ! O!&'o!o %4|#E)*xii7> ϐ= 85R1+HB&s I >1HF'0'Lʱ(97N)UT(քehg^h<),1B Θ jKhSKCc `0HdpIB1a,wT/ dAthM0HlIr2Ix^"5$Q#C71Pҟ,&Ɂl0lJ$DBޙِ$E= G~$ǀk"d0hr! lYOI M$nit!x[Ep.^HHd4T'DƄ:&T%/!z4-ߤFCԚưIlj78|7:A%)Iw/ ߳oIߤWLHM<<%HR(T ~QLKb[[&EE 4bIb] R qۑ1"J (GHh[,-Ċx@meZ"L0I{3%9tbb8NMAͺ۟Paʼni?~@8=rBc9/~hLǷ$!1AQaq? QsXAW<䜜J4*\oIUzZ @ajw+-ұKm\1eS!vA 27/~,fwiiN:#( T]˟zj+Kkfl(a9㸰M7ܫ*`"j5XQDpH N@9]LQLؗv'dMR``%V= swcI>YlN_S`әV/:iefj|^C,NKϑ}ž[P<XQV&״!֥kTߨI˥)xmqLZD#F{Q4b kY-Ա([k'1Xj"~n~|WdB#="+'CQ]D:/p̳ nP̾!/f`ԡ\ķ8=6w 9lǰ@/Q0S]7:솳)bCFAsܶJ.L0Of`Qع>.X>L!F5͆Xx`ST"@Ծ.SD|Y&ik-nU9\n*TQNCRLVzYB7SLEDbiF}P" 1ܩMyW o03a@EeAu>T  q58dR'G#' V򅓬FsH` DhΦ>ck"6ru(QWD6:fzǨqPe9%D F>%8D7,E0ٜ";}%^6a~ʬ˚V q)B-InÎH U ft*?Fe⢲]3D "bYY[?>PyJc6aٻ59 olF  .jrgй\3Wj^Y+gk^0G|+ E%1۲[7cscR $8 z3OS L"ΘbT } Ա9*8cs B+fXx8Uƅ\\1Pp:(fH>IГp ^"5,SG)PUm o _2LX2vD٨rM**FD*!e+Ɇ\ CQPUFle#!mX3,e*b6JۊTEأETt,8b3QNctsnJfQ ԬXmY.8KP1L cMj\a2R e,pB- Vlj9sImEp5G`gZ膥ne :gt6s_5h:>?wu9pexif-0.15/test/data/rose.txt000066400000000000000000000144341252620274100161620ustar00rootroot00000000000000 Section: [ APP0] Size: 14 Section: [ EXIF] Size: 6441 <--- TIFF Ifd start ---> Camera Make Canon Camera Model Canon DIGITAL IXUS II Orientation of image 1 X Resolution 180 / 1 Y Resolution 180 / 1 Unit of X and Y resolution 2 File change data and time 2006:01:14 15:35:54 Y and C positioning 1 <--- Extended EXIF start ---> Exposure Time 1 / 500 F Number 80 / 10 Exif Version ['0', '2', '2', '0'] Date of original data generation 2006:01:14 15:35:54 Date of digital data generation 2006:01:14 15:35:54 Meaning of each component ['\x01', '\x02', '\x03', '\x00'] Image compression mode 3 / 1 Shutter speed 287 / 32 Aperture 192 / 32 Exposure bias -3 / 3 Maximum lens apeture 107 / 32 Metering mode 2 Flash 16 Lens focal length 215 / 32 <--- Canon start ---> 0x1 [92, 1, 0, 3, 0, 0, 0, 4, 0, 1, 0, 1, 0, 0, 0, 0, 19, 5, 3, 16385, 0, 0, 65535, 346, 173, 32, 106, 190, 0, 0, 0, 0, 0, 1, 65535, 0, 2048, 2048, 0, 0, 2, 0, 32767, 0, 0, 0] 0x2 [2, 215, 213, 159] 0x3 [1024, 0, 0, 0] 0x4 [68, 0, 224, 221, 192, 287, 65504, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 27, 0, 190, 288, 0, 0, 0, 250, 0, 0, 0, 0, 0, 0, 13] 0x0 [0, 0, 0, 0, 0, 0] 0x0 [0, 0, 0, 0] 0x12 [9, 9, 2048, 1536, 2048, 256, 369, 42, 65166, 0, 370, 65166, 0, 370, 65166, 0, 370, 65488, 65488, 65488, 0, 0, 0, 48, 48, 48, 0, 0] 0x13 [0, 0, 0, 0] Image Type IMG:DIGITAL IXUS II JPEG Firmware Revision Firmware Version 2.01 Image Number 1080836 Owner Name 0x10 19070976 0xd [68, 9, 505, 502, 505, 505, 505, 503, 505, 499, 505, 64, 0, 0, 95, 1, 0, 10, 0, 0, 0, 23, 264, 1, 0, 1010, 1001, 0, 0, 0, 0, 146, 0, 65440] <--- Canon end ---> User comments ['\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'] Supported Flashpix version ['0', '1', '0', '0'] Color Space Information 1 Valid image width 2048 Valid image height 1536 0xa005 6186 Focal plane X resolution 2048000 / 208 Focal plane Y resolution 1536000 / 156 Focal plane resolution unit 2 Sensing method 2 File source  Customer image processing 0 Exposure mode 1 White balance 1 Digital zoom ratio 2048 / 2048 Scene capture type 0 <--- Extended EXIF end ---> <--- TIFF Ifd end ---> <--- Thumbnail start ---> Image width 200 Image height 150 Compression Scheme 6 Orientation of image 1 Offset to JPEG SOI 1624 Bytes of JPEG data 4811 <--- Thumbnail end ---> Section: [ DQT] Size: 65 Section: [ DQT] Size: 65 Section: [ SOF2] Size: 15 Section: [ DHT] Size: 24 Section: [ DHT] Size: 22 Section: [ SOS] Size: 10 Image data size: 4023 pexif-0.15/test/test.py000066400000000000000000000205471252620274100150730ustar00rootroot00000000000000import unittest import pexif import StringIO import difflib test_data = [ ("test/data/rose.jpg", "test/data/rose.txt"), ("test/data/conker.jpg", "test/data/conker.txt"), ("test/data/noexif.jpg", "test/data/noexif.txt"), ] DEFAULT_TESTFILE = test_data[0][0] NONEXIST_TESTFILE = "test/data/noexif.jpg" class TestLoadFunctions(unittest.TestCase): def test_fromFile(self): # Simple test ensures we can load and parse a file from filename for test_file, _ in test_data: pexif.JpegFile.fromFile(test_file) def test_fromString(self): # Simple test ensures we can load and parse a file passed as a string for test_file, _ in test_data: fd = open(test_file, "rb") data = fd.read() fd.close() pexif.JpegFile.fromString(data) def test_fromFd(self): # Simple test ensure we can load and parse a file passed as a fd for test_file, _ in test_data: fd = open(test_file, "rb") pexif.JpegFile.fromFd(fd) def test_emptyData(self): # Simple test ensures that empty string fails self.assertRaises(pexif.JpegFile.InvalidFile, pexif.JpegFile.fromString, "") def test_badData(self): # Simple test ensures that random crap doesn't get parsed self.assertRaises(pexif.JpegFile.InvalidFile, pexif.JpegFile.fromString, "asl;dkfjasl;kdjfsld") def test_regen(self): # Test to ensure the new file matches the existing file for test_file, _ in test_data: data = open(test_file, "rb").read() jpeg = pexif.JpegFile.fromString(data) new_data = jpeg.writeString() self.assertEqual(data, new_data, "Binary differs for <%s>" % test_file) def test_dump(self): # Test that the dumped data is as expected. for test_file, expected_file in test_data: expected = open(expected_file, 'rb').read() jpeg = pexif.JpegFile.fromFile(test_file) out = StringIO.StringIO() jpeg.dump(out) res = "Error in file <%s>\n" % test_file x = difflib.unified_diff(expected.split('\n'), out.getvalue().split('\n')) for each in x: res += each res += '\n' self.assertEqual(expected, out.getvalue(), res) class TestExifFunctions(unittest.TestCase): def test_badendian(self): data = list(open(DEFAULT_TESTFILE, "rb").read()) # Now trash the exif signature assert(data[0x1E] == 'I') data[0x1E] = '0' self.assertRaises(pexif.JpegFile.InvalidFile, pexif.JpegFile.fromString, "".join(data)) def test_badtifftag(self): data = list(open(DEFAULT_TESTFILE, "rb").read()) # Now trash the exif signature assert(data[0x20] == '\x2a') data[0x20] = '0' self.assertRaises(pexif.JpegFile.InvalidFile, pexif.JpegFile.fromString, "".join(data)) def test_goodexif(self): for test_file, _ in test_data: jp = pexif.JpegFile.fromFile(test_file) jp.get_exif() def test_noexif(self): jp = pexif.JpegFile.fromFile(NONEXIST_TESTFILE) self.assertEqual(jp.get_exif(), None) def test_noexif_create(self): jp = pexif.JpegFile.fromFile(NONEXIST_TESTFILE) self.assertNotEqual(jp.get_exif(create=True), None) def test_getattr_nonexist(self): for test_file, _ in test_data: attr = pexif.JpegFile.fromFile(test_file). \ get_exif(create=True). \ get_primary(create=True) self.assertEqual(attr["ImageWidth"], None) def foo(): attr.ImageWidth self.assertRaises(AttributeError, foo) def test_getattr_exist(self): attr = pexif.JpegFile.fromFile(DEFAULT_TESTFILE).get_exif().get_primary() self.assertEqual(attr["Make"], "Canon") self.assertEqual(attr.Make, "Canon") def test_setattr_nonexist(self): for test_file, _ in test_data: attr = pexif.JpegFile.fromFile(test_file). \ get_exif(create=True).get_primary(create=True) attr["ImageWidth"] = 3 self.assertEqual(attr["ImageWidth"], 3) def test_setattr_exist(self): for test_file, _ in test_data: attr = pexif.JpegFile.fromFile(test_file). \ get_exif(create=True). \ get_primary(create=True) attr.Make = "CanonFoo" self.assertEqual(attr.Make, "CanonFoo") attr["Make"] = "CanonFoo" self.assertEqual(attr["Make"], "CanonFoo") def test_setattr_exist_none(self): for test_file, _ in test_data: attr = pexif.JpegFile.fromFile(test_file). \ get_exif(create=True). \ get_primary(create=True) attr["Make"] = None self.assertEqual(attr["Make"], None) attr.Make = "Foo" self.assertEqual(attr["Make"], "Foo") del attr.Make self.assertEqual(attr["Make"], None) def test_add_geo(self): for test_file, _ in test_data: jf = pexif.JpegFile.fromFile(test_file) try: jf.get_geo() return except jf.NoSection: pass attr = jf.get_exif(create=True).get_primary(create=True) gps = attr.new_gps() gps["GPSLatitudeRef"] = "S" gps["GPSLongitudeRef"] = "E" data = jf.writeString() jf2 = pexif.JpegFile.fromString(data) self.assertEqual(jf2.get_exif().get_primary().GPS \ ["GPSLatitudeRef"], "S") def test_simple_add_geo(self): for test_file, _ in test_data: jf = pexif.JpegFile.fromFile(test_file) (lat, lng) = (-37.312312, 45.412321) jf.set_geo(lat, lng) new_file = jf.writeString() new = pexif.JpegFile.fromString(new_file) new_lat, new_lng = new.get_geo() self.assertAlmostEqual(lat, new_lat, 6) self.assertAlmostEqual(lng, new_lng, 6) def test_simple_add_geo2(self): for test_file, _ in test_data: jf = pexif.JpegFile.fromFile(test_file) (lat, lng) = (51.522, -1.455) jf.set_geo(lat, lng) new_file = jf.writeString() new = pexif.JpegFile.fromString(new_file) new_lat, new_lng = new.get_geo() self.assertAlmostEqual(lat, new_lat, 6) self.assertAlmostEqual(lng, new_lng, 6) def test_simple_add_geo3(self): for test_file, _ in test_data: jf = pexif.JpegFile.fromFile(test_file) (lat, lng) = (51.522, -1.2711) jf.set_geo(lat, lng) new_file = jf.writeString() new = pexif.JpegFile.fromString(new_file) new_lat, new_lng = new.get_geo() self.assertAlmostEqual(lat, new_lat, 6) self.assertAlmostEqual(lng, new_lng, 6) def test_get_geo(self): jf = pexif.JpegFile.fromFile(DEFAULT_TESTFILE) self.assertRaises(pexif.JpegFile.NoSection, jf.get_geo) def test_exif_property(self): def test_get(): foo = jf.exif jf = pexif.JpegFile.fromFile(DEFAULT_TESTFILE, mode="ro") self.assertEqual(jf.exif.__class__, pexif.ExifSegment) # exif doesn't exist jf = pexif.JpegFile.fromFile(NONEXIST_TESTFILE, mode="ro") self.assertRaises(AttributeError, test_get) def test_invalid_set(self): """Test that setting an invalid tag raise an attribute error""" jf = pexif.JpegFile.fromFile(DEFAULT_TESTFILE) def test_set(): jf.exif.primary.UserComment = "foobar" self.assertRaises(AttributeError, test_set) def test_invalid_set_embedded(self): """Test that setting an embedded tag raises a type error""" jf = pexif.JpegFile.fromFile(DEFAULT_TESTFILE) def test_set(): jf.exif.primary.ExtendedEXIF = 5 self.assertRaises(TypeError, test_set) def test_set_embedded(self): """Test that setting an embedded tag raises a type error""" jf = pexif.JpegFile.fromFile(DEFAULT_TESTFILE) ext_exif = pexif.IfdExtendedEXIF(jf.exif.primary.e, 0, "rw", jf) jf.exif.primary.ExtendedEXIF = ext_exif if __name__ == "__main__": unittest.main()