From ce53b1b01816cb7f2018189f5a0bf20256b21449 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Sat, 10 Mar 2012 19:12:33 -0800 Subject: [PATCH 01/54] CFF Parser and Compiler. --- src/fonts.js | 1495 +++++++++++++++++++++++++++------------ test/pdfs/.gitignore | 1 + test/pdfs/issue1002.pdf | Bin 0 -> 19172 bytes test/test_manifest.json | 7 + test/unit/font_spec.js | 223 ++++++ 5 files changed, 1292 insertions(+), 434 deletions(-) create mode 100644 test/pdfs/issue1002.pdf create mode 100644 test/unit/font_spec.js diff --git a/src/fonts.js b/src/fonts.js index 542c33f55..547eae77f 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -829,6 +829,8 @@ var Font = (function FontClosure() { return; } + this.loadedName = getUniqueName(); + properties.id = this.loadedName; var data; switch (type) { case 'Type1': @@ -837,7 +839,7 @@ var Font = (function FontClosure() { var subtype = properties.subtype; var cff = (subtype == 'Type1C' || subtype == 'CIDFontType0C') ? - new Type2CFF(file, properties) : new CFF(name, file, properties); + new CFF(file, properties) : new Type1Font(name, file, properties); // Wrap the CFF data inside an OTF font file data = this.convert(name, cff, properties); @@ -862,7 +864,6 @@ var Font = (function FontClosure() { this.widthMultiplier = !properties.fontMatrix ? 1.0 : 1.0 / properties.fontMatrix[0]; this.encoding = properties.baseEncoding; - this.loadedName = getUniqueName(); this.loading = true; }; @@ -2931,7 +2932,7 @@ var Type1Parser = function type1Parser() { * The CFF class takes a Type1 file and wrap it into a * 'Compact Font Format' which itself embed Type2 charstrings. */ -var CFFStrings = [ +var CFFStandardStrings = [ '.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', @@ -3001,7 +3002,8 @@ var CFFStrings = [ var type1Parser = new Type1Parser(); -var CFF = function cffCFF(name, file, properties) { +// Type1Font is also a CIDFontType0. +var Type1Font = function Type1Font(name, file, properties) { // Get the data block containing glyphs and subrs informations var headerBlock = file.getBytes(properties.length1); type1Parser.extractFontHeader(headerBlock, properties); @@ -3021,8 +3023,8 @@ var CFF = function cffCFF(name, file, properties) { subrs, properties); }; -CFF.prototype = { - createCFFIndexHeader: function cff_createCFFIndexHeader(objects, isByte) { +Type1Font.prototype = { + createCFFIndexHeader: function createCFFIndexHeader(objects, isByte) { // First 2 bytes contains the number of objects contained into this index var count = objects.length; @@ -3058,7 +3060,7 @@ CFF.prototype = { return data; }, - encodeNumber: function cff_encodeNumber(value) { + encodeNumber: function encodeNumber(value) { // some of the fonts has ouf-of-range values // they are just arithmetic overflows // make sanitizer happy @@ -3076,7 +3078,7 @@ CFF.prototype = { } }, - getOrderedCharStrings: function cff_getOrderedCharStrings(glyphs, + getOrderedCharStrings: function type1Font_getOrderedCharStrings(glyphs, properties) { var charstrings = []; var i, length, glyphName; @@ -3102,7 +3104,7 @@ CFF.prototype = { return charstrings; }, - getType2Charstrings: function cff_getType2Charstrings(type1Charstrings) { + getType2Charstrings: function getType2Charstrings(type1Charstrings) { var type2Charstrings = []; var count = type1Charstrings.length; for (var i = 0; i < count; i++) { @@ -3113,7 +3115,7 @@ CFF.prototype = { return type2Charstrings; }, - getType2Subrs: function cff_getType2Subrs(type1Subrs) { + getType2Subrs: function getType2Subrs(type1Subrs) { var bias = 0; var count = type1Subrs.length; if (count < 1240) @@ -3261,7 +3263,7 @@ CFF.prototype = { var count = glyphs.length; for (var i = 0; i < count; i++) { - var index = CFFStrings.indexOf(charstrings[i].glyph); + var index = CFFStandardStrings.indexOf(charstrings[i].glyph); // Some characters like asterikmath && circlecopyrt are // missing from the original strings, for the moment let's // map them to .notdef and see later if it cause any @@ -3330,106 +3332,31 @@ CFF.prototype = { } }; -var Type2CFF = (function Type2CFFClosure() { - // TODO: replace parsing code with the Type2Parser in font_utils.js - function Type2CFF(file, properties) { - var bytes = file.getBytes(); - this.bytes = bytes; +var CFF = (function CFFClosure() { + function CFF(file, properties) { this.properties = properties; - this.data = this.parse(); + var parser = new CFFParser(file, properties); + var cff = parser.parse(); + var compiler = new CFFCompiler(cff); + this.readExtra(cff); + try { + this.data = compiler.compile(); + } catch (e) { + warn('Failed to compile font ' + properties.loadedName); + // There may have just been an issue with the compiler, set the data + // anyway and hope the font loaded. + this.data = file; + } } - Type2CFF.prototype = { - parse: function cff_parse() { - var header = this.parseHeader(); - var properties = this.properties; - - var nameIndex = this.parseIndex(header.endPos); - this.sanitizeName(nameIndex); - - var dictIndex = this.parseIndex(nameIndex.endPos); - if (dictIndex.length != 1) - error('CFF contains more than 1 font'); - - var stringIndex = this.parseIndex(dictIndex.endPos); - var gsubrIndex = this.parseIndex(stringIndex.endPos); - - var strings = this.getStrings(stringIndex); - - var baseDict = this.parseDict(dictIndex.get(0).data); - var topDict = this.getTopDict(baseDict, strings); - - var bytes = this.bytes; - - var privateDict = {}; - var privateInfo = topDict.Private; - if (privateInfo) { - var privOffset = privateInfo[1], privLength = privateInfo[0]; - var privBytes = bytes.subarray(privOffset, privOffset + privLength); - baseDict = this.parseDict(privBytes); - privateDict = this.getPrivDict(baseDict, strings); - } else { - privateDict.defaultWidthX = properties.defaultWidth; - } - - var charStrings = this.parseIndex(topDict.CharStrings); - - var charset, encoding; - var isCIDFont = properties.subtype == 'CIDFontType0C'; - if (isCIDFont) { - charset = ['.notdef']; - for (var i = 1, ii = charStrings.length; i < ii; ++i) - charset.push('glyph' + i); - - encoding = this.parseCidMap(topDict.charset, - charStrings.length); - } else { - charset = this.parseCharsets(topDict.charset, - charStrings.length, strings); - encoding = this.parseEncoding(topDict.Encoding, properties, - strings, charset); - } - - // The font sanitizer does not support CFF encoding with a - // supplement, since the encoding is not really use to map - // between gid to glyph, let's overwrite what is declared in - // the top dictionary to let the sanitizer think the font use - // StandardEncoding, that's a lie but that's ok. - if (encoding.hasSupplement) - bytes[topDict.Encoding] &= 0x7F; - - // The CFF specification state that the 'dotsection' command - // (12, 0) is deprecated and treated as a no-op, but all Type2 - // charstrings processors should support them. Unfortunately - // the font sanitizer don't. As a workaround the sequence (12, 0) - // is replaced by a useless (0, hmoveto). - var count = charStrings.length; - for (var i = 0; i < count; i++) { - var charstring = charStrings.get(i); - - var start = charstring.start; - var data = charstring.data; - var length = data.length; - for (var j = 0; j <= length; j) { - var value = data[j++]; - if (value == 12 && data[j++] == 0) { - bytes[start + j - 2] = 139; - bytes[start + j - 1] = 22; - } else if (value === 28) { - j += 2; - } else if (value >= 247 && value <= 254) { - j++; - } else if (value == 255) { - j += 4; - } - } - } - + CFF.prototype = { + readExtra: function readExtra(cff) { // charstrings contains info about glyphs (one element per glyph // containing mappings for {unicode, width}) - var charstrings = this.getCharStrings(charset, encoding.encoding, - privateDict, this.properties); + var charset = cff.charset.charset; + var encoding = cff.encoding ? cff.encoding.encoding : null; + var charstrings = this.getCharStrings(charset, encoding); // create the mapping between charstring and glyph id var glyphIds = []; @@ -3438,21 +3365,18 @@ var Type2CFF = (function Type2CFFClosure() { this.charstrings = charstrings; this.glyphIds = glyphIds; - - var data = []; - for (var i = 0, ii = bytes.length; i < ii; ++i) - data.push(bytes[i]); - return data; }, - - getCharStrings: function cff_charstrings(charsets, encoding, - privateDict, properties) { + getCharStrings: function getCharStrings(charsets, encoding) { var charstrings = []; var unicodeUsed = []; var unassignedUnicodeItems = []; var inverseEncoding = []; - for (var charcode in encoding) - inverseEncoding[encoding[charcode]] = charcode | 0; + // CID fonts don't have an encoding. + if (encoding !== null) + for (var charcode in encoding) + inverseEncoding[encoding[charcode]] = charcode | 0; + else + inverseEncoding = charsets; for (var i = 0, ii = charsets.length; i < ii; i++) { var glyph = charsets[i]; if (glyph == '.notdef') @@ -3488,284 +3412,80 @@ var Type2CFF = (function Type2CFFClosure() { } // sort the array by the unicode value (again) - charstrings.sort(function type2CFFGetCharStringsSort(a, b) { + charstrings.sort(function getCharStringsSort(a, b) { return a.unicode - b.unicode; }); return charstrings; - }, + } + }; - parseEncoding: function cff_parseencoding(pos, properties, strings, - charset) { - var encoding = {}; - var bytes = this.bytes; - var result = { - encoding: encoding, - hasSupplement: false - }; + return CFF; +})(); - function readSupplement() { - var supplementsCount = bytes[pos++]; - for (var i = 0; i < supplementsCount; i++) { - var code = bytes[pos++]; - var sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff); - encoding[code] = properties.differences.indexOf(strings[sid]); - } - } - - if (pos == 0 || pos == 1) { - var gid = 1; - var baseEncoding = pos ? Encodings.ExpertEncoding : - Encodings.StandardEncoding; - for (var i = 0, ii = charset.length; i < ii; i++) { - var index = baseEncoding.indexOf(charset[i]); - if (index != -1) - encoding[index] = gid++; +var CFFParser = (function CFFParserClosure() { + function CFFParser(file, properties) { + this.bytes = file.getBytes(); + this.properties = properties; + } + CFFParser.prototype = { + parse: function parse() { + var properties = this.properties; + var cff = new CFFTable(); + this.cff = cff; + + // The first five sections must be in order, all the others are reached + // via offsets contained in one of the below. + var header = this.parseHeader(); + var nameIndex = this.parseIndex(header.endPos); + var topDictIndex = this.parseIndex(nameIndex.endPos); + var stringIndex = this.parseIndex(topDictIndex.endPos); + var globalSubrIndex = this.parseIndex(stringIndex.endPos); + + var topDictParsed = this.parseDict(topDictIndex.obj.get(0)); + var topDict = this.createDict(CFFTopDict, topDictParsed, cff.strings); + + cff.header = header.obj; + cff.names = this.parseNameIndex(nameIndex.obj); + cff.strings = this.parseStringIndex(stringIndex.obj); + cff.topDict = topDict; + cff.globalSubrIndex = globalSubrIndex.obj; + + this.parsePrivateDict(cff.topDict); + + cff.isCIDFont = topDict.hasName('ROS'); + + var charStringOffset = topDict.getByName('CharStrings'); + cff.charStrings = this.parseCharStrings(charStringOffset); + + var charset, encoding; + if (cff.isCIDFont) { + var fdArrayIndex = this.parseIndex(topDict.getByName('FDArray')).obj; + for (var i = 0, ii = fdArrayIndex.count; i < ii; ++i) { + var dictRaw = fdArrayIndex.get(i); + var fontDict = this.createDict(CFFTopDict, this.parseDict(dictRaw), + cff.strings); + this.parsePrivateDict(fontDict); + cff.fdArray.push(fontDict); } + // cid fonts don't have an encoding + encoding = null; + charset = this.parseCharsets(topDict.getByName('charset'), + cff.charStrings.count, cff.strings, true); + cff.fdSelect = this.parseFDSelect(topDict.getByName('FDSelect'), + cff.charStrings.count); } else { - var format = bytes[pos++]; - switch (format & 0x7f) { - case 0: - var glyphsCount = bytes[pos++]; - for (var i = 1; i <= glyphsCount; i++) - encoding[bytes[pos++]] = i; - break; - - case 1: - var rangesCount = bytes[pos++]; - var gid = 1; - for (var i = 0; i < rangesCount; i++) { - var start = bytes[pos++]; - var left = bytes[pos++]; - for (var j = start; j <= start + left; j++) - encoding[j] = gid++; - } - break; - - default: - error('Unknow encoding format: ' + format + ' in CFF'); - break; - } - if (format & 0x80) { - readSupplement(); - result.hasSupplement = true; - } + charset = this.parseCharsets(topDict.getByName('charset'), + cff.charStrings.count, cff.strings, false); + encoding = this.parseEncoding(topDict.getByName('Encoding'), + properties, + cff.strings, charset.charset); } - return result; + cff.charset = charset; + cff.encoding = encoding; + + return cff; }, - - parseCharsets: function cff_parsecharsets(pos, length, strings) { - if (pos == 0) { - return ISOAdobeCharset.slice(); - } else if (pos == 1) { - return ExpertCharset.slice(); - } else if (pos == 2) { - return ExpertSubsetCharset.slice(); - } - - var bytes = this.bytes; - var format = bytes[pos++]; - var charset = ['.notdef']; - - // subtract 1 for the .notdef glyph - length -= 1; - - switch (format) { - case 0: - for (var i = 0; i < length; i++) { - var sid = (bytes[pos++] << 8) | bytes[pos++]; - charset.push(strings[sid]); - } - break; - case 1: - while (charset.length <= length) { - var sid = (bytes[pos++] << 8) | bytes[pos++]; - var count = bytes[pos++]; - for (var i = 0; i <= count; i++) - charset.push(strings[sid++]); - } - break; - case 2: - while (charset.length <= length) { - var sid = (bytes[pos++] << 8) | bytes[pos++]; - var count = (bytes[pos++] << 8) | bytes[pos++]; - for (var i = 0; i <= count; i++) - charset.push(strings[sid++]); - } - break; - default: - error('Unknown charset format'); - } - return charset; - }, - - parseCidMap: function cff_parsecharsets(pos, length) { - var bytes = this.bytes; - var format = bytes[pos++]; - - var encoding = {}; - var map = {encoding: encoding}; - - encoding[0] = 0; - - var gid = 1; - switch (format) { - case 0: - while (gid < length) { - var cid = (bytes[pos++] << 8) | bytes[pos++]; - encoding[cid] = gid++; - } - break; - case 1: - while (gid < length) { - var cid = (bytes[pos++] << 8) | bytes[pos++]; - var count = bytes[pos++]; - for (var i = 0; i <= count; i++) - encoding[cid++] = gid++; - } - break; - case 2: - while (gid < length) { - var cid = (bytes[pos++] << 8) | bytes[pos++]; - var count = (bytes[pos++] << 8) | bytes[pos++]; - for (var i = 0; i <= count; i++) - encoding[cid++] = gid++; - } - break; - default: - error('Unknown charset format'); - } - return map; - }, - - getPrivDict: function cff_getprivdict(baseDict, strings) { - var dict = {}; - - // default values - dict['defaultWidthX'] = 0; - dict['nominalWidthX'] = 0; - - for (var i = 0, ii = baseDict.length; i < ii; ++i) { - var pair = baseDict[i]; - var key = pair[0]; - var value = pair[1]; - switch (key) { - case 20: - dict['defaultWidthX'] = value[0]; - case 21: - dict['nominalWidthX'] = value[0]; - default: - TODO('interpret top dict key: ' + key); - } - } - return dict; - }, - getTopDict: function cff_gettopdict(baseDict, strings) { - var dict = {}; - - // default values - dict['Encoding'] = 0; - dict['charset'] = 0; - - for (var i = 0, ii = baseDict.length; i < ii; ++i) { - var pair = baseDict[i]; - var key = pair[0]; - var value = pair[1]; - switch (key) { - case 1: - dict['Notice'] = strings[value[0]]; - break; - case 4: - dict['Weight'] = strings[value[0]]; - break; - case 3094: - dict['BaseFontName'] = strings[value[0]]; - break; - case 5: - dict['FontBBox'] = value; - break; - case 13: - dict['UniqueID'] = value[0]; - break; - case 15: - dict['charset'] = value[0]; - break; - case 16: - dict['Encoding'] = value[0]; - break; - case 17: - dict['CharStrings'] = value[0]; - break; - case 18: - dict['Private'] = value; - break; - case 3102: - case 3103: - case 3104: - case 3105: - case 3106: - case 3107: - case 3108: - case 3109: - case 3110: - dict['cidOperatorPresent'] = true; - break; - default: - TODO('interpret top dict key: ' + key); - } - } - return dict; - }, - sanitizeName: function cff_sanitizeName(nameIndex) { - // There should really only be one font, but loop to make sure. - for (var i = 0, ii = nameIndex.length; i < ii; ++i) { - var data = nameIndex.get(i).data; - var length = data.length; - if (length > 127) - warn('Font had name longer than 127 chars, will be rejected.'); - // Only certain chars are permitted in the font name. - for (var j = 0; j < length; ++j) { - var c = data[j]; - if (j === 0 && c === 0) - continue; - if (c < 33 || c > 126) { - data[j] = 95; - continue; - } - switch (c) { - case 91: // [ - case 93: // ] - case 40: // ( - case 41: // ) - case 123: // { - case 125: // } - case 60: // < - case 62: // > - case 47: // / - case 37: // % - data[j] = 95; - break; - } - } - } - }, - getStrings: function cff_getStrings(stringIndex) { - function bytesToString(bytesArray) { - var str = ''; - for (var i = 0, ii = bytesArray.length; i < ii; i++) - str += String.fromCharCode(bytesArray[i]); - return str; - } - - var stringArray = []; - for (var i = 0, ii = CFFStrings.length; i < ii; i++) - stringArray.push(CFFStrings[i]); - - for (var i = 0, ii = stringIndex.length; i < ii; i++) - stringArray.push(bytesToString(stringIndex.get(i).data)); - - return stringArray; - }, - parseHeader: function cff_parseHeader() { + parseHeader: function parseHeader() { var bytes = this.bytes; var offset = 0; @@ -3773,17 +3493,18 @@ var Type2CFF = (function Type2CFFClosure() { ++offset; if (offset != 0) { - warning('cff data is shifted'); + warn('cff data is shifted'); bytes = bytes.subarray(offset); this.bytes = bytes; } - - return { - endPos: bytes[2], - offsetSize: bytes[3] - }; + var major = bytes[0]; + var minor = bytes[1]; + var hdrSize = bytes[2]; + var offSize = bytes[3]; + var header = new CFFHeader(major, minor, hdrSize, offSize); + return {obj: header, endPos: hdrSize}; }, - parseDict: function cff_parseDict(dict) { + parseDict: function parseDict(dict) { var pos = 0; function parseOperand() { @@ -3800,11 +3521,11 @@ var Type2CFF = (function Type2CFFClosure() { value = (value << 8) | dict[pos++]; value = (value << 8) | dict[pos++]; return value; - } else if (value <= 246) { + } else if (value >= 32 && value <= 246) { return value - 139; - } else if (value <= 250) { + } else if (value >= 247 && value <= 250) { return ((value - 247) * 256) + dict[pos++] + 108; - } else if (value <= 254) { + } else if (value >= 251 && value <= 254) { return -((value - 251) * 256) - dict[pos++] - 108; } else { error('255 is not a valid DICT command'); @@ -3842,27 +3563,8 @@ var Type2CFF = (function Type2CFFClosure() { while (pos < end) { var b = dict[pos]; if (b <= 21) { - if (b === 12) { - ++pos; - var op = dict[pos]; - if ((op > 14 && op < 17) || - (op > 23 && op < 30) || op > 38) { - warn('Invalid CFF dictionary key: ' + op); - // trying to replace it with initialRandomSeed - // to pass sanitizer - dict[pos] = 19; - } - var b = (b << 8) | op; - } - if (!operands.length && b == 8 && - dict[pos + 1] == 9) { - // no operands for FamilyBlues, removing the key - // and next one is FamilyOtherBlues - skipping them - // also replacing FamilyBlues to pass sanitizer - dict[pos] = 139; - pos += 2; - continue; - } + if (b === 12) + b = (b << 8) | dict[++pos]; entries.push([b, operands]); operands = []; ++pos; @@ -3872,10 +3574,12 @@ var Type2CFF = (function Type2CFFClosure() { } return entries; }, - parseIndex: function cff_parseIndex(pos) { + parseIndex: function parseIndex(pos) { + var cffIndex = new CFFIndex(); var bytes = this.bytes; - var count = bytes[pos++] << 8 | bytes[pos++]; + var count = (bytes[pos++] << 8) | bytes[pos++]; var offsets = []; + var start = pos; var end = pos; if (count != 0) { @@ -3893,26 +3597,949 @@ var Type2CFF = (function Type2CFFClosure() { } end = offsets[count]; } + for (var i = 0, ii = offsets.length - 1; i < ii; ++i) { + var offsetStart = offsets[i]; + var offsetEnd = offsets[i + 1]; + cffIndex.add(bytes.subarray(offsetStart, offsetEnd)); + } + return {obj: cffIndex, endPos: end}; + }, + parseNameIndex: function parseNameIndex(index) { + var names = []; + for (var i = 0, ii = index.count; i < ii; ++i) { + var name = index.get(i); + // OTS doesn't allow names to be over 127 characters. + var length = Math.min(name.length, 127); + var data = new Array(length); + // OTS also only permits certain characters in the name. + for (var j = 0; j < length; ++j) { + var c = name[j]; + if (j === 0 && c === 0) { + data[j] = c; + continue; + } + if ((c < 33 || c > 126) || c === 91 /* [ */ || c === 93 /* ] */ || + c === 40 /* ( */ || c === 41 /* ) */ || c === 123 /* { */ || + c === 125 /* } */ || c === 60 /* < */ || c === 62 /* > */ || + c === 47 /* / */ || c === 37 /* % */) { + data[j] = 95; + continue; + } + data[j] = c; + } + names.push(String.fromCharCode.apply(null, data)); + } + return names; + }, + parseStringIndex: function parseStringIndex(index) { + var strings = new CFFStrings(); + for (var i = 0, ii = index.count; i < ii; ++i) { + var data = index.get(i); + strings.add(String.fromCharCode.apply(null, data)); + } + return strings; + }, + createDict: function createDict(type, dict, strings) { + var cffDict = new type(strings); + var types = cffDict.types; - return { - get: function index_get(index) { - if (index >= count) - return null; + for (var i = 0, ii = dict.length; i < ii; ++i) { + var pair = dict[i]; + var key = pair[0]; + var value = pair[1]; + cffDict.setByKey(key, value); + } + return cffDict; + }, + parseCharStrings: function parseCharStrings(charStringOffset) { + var charStrings = this.parseIndex(charStringOffset).obj; + // The CFF specification state that the 'dotsection' command + // (12, 0) is deprecated and treated as a no-op, but all Type2 + // charstrings processors should support them. Unfortunately + // the font sanitizer don't. As a workaround the sequence (12, 0) + // is replaced by a useless (0, hmoveto). + var count = charStrings.count; + for (var i = 0; i < count; i++) { + var charstring = charStrings.get(i); - var start = offsets[index]; - var end = offsets[index + 1]; - return { - start: start, - end: end, - data: bytes.subarray(start, end) - }; - }, - length: count, - endPos: end - }; + var data = charstring; + var length = data.length; + for (var j = 0; j <= length; j) { + var value = data[j++]; + if (value == 12 && data[j++] == 0) { + data[j - 2] = 139; + data[j - 1] = 22; + } else if (value === 28) { + j += 2; + } else if (value >= 247 && value <= 254) { + j++; + } else if (value == 255) { + j += 4; + } + } + } + return charStrings; + }, + parsePrivateDict: function parsePrivateDict(parentDict) { + // no private dict, do nothing + if (!parentDict.hasName('Private')) + return; + var privateOffset = parentDict.getByName('Private'); + // make sure the params are formatted correctly + if (!isArray(privateOffset) || privateOffset.length !== 2) { + parentDict.removeByName('Private'); + return; + } + var size = privateOffset[0]; + var offset = privateOffset[1]; + // remove empty dicts or ones that refer to invalid location + if (size === 0 || offset >= this.bytes.length) { + parentDict.removeByName('Private'); + return; + } + + var privateDictEnd = offset + size; + var dictData = this.bytes.subarray(offset, privateDictEnd); + var dict = this.parseDict(dictData); + var privateDict = this.createDict(CFFPrivateDict, dict, + parentDict.strings); + parentDict.privateDict = privateDict; + + // Parse the Subrs index also since it's relative to the private dict. + if (!privateDict.getByName('Subrs')) + return; + var subrsOffset = privateDict.getByName('Subrs'); + var relativeOffset = offset + subrsOffset; + // Validate the offset. + if (subrsOffset === 0 || relativeOffset >= this.bytes.length) { + privateDict.removeByName('Subrs'); + return; + } + var subrsIndex = this.parseIndex(relativeOffset); + privateDict.subrsIndex = subrsIndex.obj; + }, + parseCharsets: function parsecharsets(pos, length, strings, cid) { + if (pos == 0) { + return new CFFCharset(true, CFFCharsetPredefinedTypes.ISO_ADOBE, + ISOAdobeCharset.slice()); + } else if (pos == 1) { + return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT, + ExpertCharset.slice()); + } else if (pos == 2) { + return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT_SUBSET, + ExpertSubsetCharset.slice()); + } + + var bytes = this.bytes; + var start = pos; + var format = bytes[pos++]; + var charset = ['.notdef']; + + // subtract 1 for the .notdef glyph + length -= 1; + + switch (format) { + case 0: + for (var i = 0; i < length; i++) { + var id = (bytes[pos++] << 8) | bytes[pos++]; + charset.push(cid ? id : strings.get(id)); + } + break; + case 1: + while (charset.length <= length) { + var id = (bytes[pos++] << 8) | bytes[pos++]; + var count = bytes[pos++]; + for (var i = 0; i <= count; i++) + charset.push(cid ? id++ : strings.get(id++)); + } + break; + case 2: + while (charset.length <= length) { + var id = (bytes[pos++] << 8) | bytes[pos++]; + var count = (bytes[pos++] << 8) | bytes[pos++]; + for (var i = 0; i <= count; i++) + charset.push(cid ? id++ : strings.get(id++)); + } + break; + default: + error('Unknown charset format'); + } + // Raw won't be needed if we actually compile the charset. + var end = pos; + var raw = bytes.subarray(start, end); + + return new CFFCharset(false, format, charset, raw); + }, + parseEncoding: function parseEncoding(pos, properties, strings, charset) { + var encoding = {}; + var bytes = this.bytes; + var predefined = false; + var hasSupplement = false; + var format; + var raw = null; + + function readSupplement() { + var supplementsCount = bytes[pos++]; + for (var i = 0; i < supplementsCount; i++) { + var code = bytes[pos++]; + var sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff); + encoding[code] = properties.differences.indexOf(strings.get(sid)); + } + } + + if (pos == 0 || pos == 1) { + predefined = true; + format = pos; + var gid = 1; + var baseEncoding = pos ? Encodings.ExpertEncoding : + Encodings.StandardEncoding; + for (var i = 0, ii = charset.length; i < ii; i++) { + var index = baseEncoding.indexOf(charset[i]); + if (index != -1) + encoding[index] = gid++; + } + } else { + var dataStart = pos; + var format = bytes[pos++]; + switch (format & 0x7f) { + case 0: + var glyphsCount = bytes[pos++]; + for (var i = 1; i <= glyphsCount; i++) + encoding[bytes[pos++]] = i; + break; + + case 1: + var rangesCount = bytes[pos++]; + var gid = 1; + for (var i = 0; i < rangesCount; i++) { + var start = bytes[pos++]; + var left = bytes[pos++]; + for (var j = start; j <= start + left; j++) + encoding[j] = gid++; + } + break; + + default: + error('Unknow encoding format: ' + format + ' in CFF'); + break; + } + var dataEnd = pos; + if (format & 0x80) { + // The font sanitizer does not support CFF encoding with a + // supplement, since the encoding is not really used to map + // between gid to glyph, let's overwrite what is declared in + // the top dictionary to let the sanitizer think the font use + // StandardEncoding, that's a lie but that's ok. + bytes[dataStart] &= 0x7f; + readSupplement(); + hasSupplement = true; + } + raw = bytes.subarray(dataStart, dataEnd); + } + format = format & 0x7f; + return new CFFEncoding(predefined, format, encoding, raw); + }, + parseFDSelect: function parseFDSelect(pos, length) { + var start = pos; + var bytes = this.bytes; + var format = bytes[pos++]; + var fdSelect = []; + switch (format) { + case 0: + for (var i = 0; i < length; ++i) { + var id = bytes[pos++]; + fdSelect.push(id); + } + break; + case 3: + var rangesCount = (bytes[pos++] << 8) | bytes[pos++]; + for (var i = 0; i < rangesCount; ++i) { + var first = (bytes[pos++] << 8) | bytes[pos++]; + var fdIndex = bytes[pos++]; + var next = (bytes[pos] << 8) | bytes[pos + 1]; + for (var j = first; j < next; ++j) + fdSelect.push(fdIndex); + } + // Advance past the sentinel(next). + pos += 2; + break; + default: + error('Unknown fdselect format ' + format); + break; + } + var end = pos; + return new CFFFDSelect(fdSelect, bytes.subarray(start, end)); } }; - - return Type2CFF; + return CFFParser; +})(); + +// Compact Font Format +var CFFTable = (function CFFTableClosure() { + function CFFTable() { + this.header = null; + this.names = []; + this.topDict = null; + this.strings = new CFFStrings(); + this.globalSubrIndex = null; + + // The following could really be per font, but since we only have one font + // store them here. + this.encoding = null; + this.charset = null; + this.charStrings = null; + this.fdArray = []; + this.fdSelect = null; + + this.isCIDFont = false; + } + return CFFTable; +})(); + +var CFFHeader = (function CFFHeader() { + function CFFHeader(major, minor, hdrSize, offSize) { + this.major = major; + this.minor = minor; + this.hdrSize = hdrSize; + this.offSize = offSize; + } + return CFFHeader; +})(); + +var CFFStrings = (function CFFStrings() { + function CFFStrings() { + this.strings = []; + } + CFFStrings.prototype = { + get: function get(index) { + if (index >= 0 && index <= 390) + return CFFStandardStrings[index]; + if (index - 391 <= this.strings.length) + return this.strings[index - 391]; + return CFFStandardStrings[0]; + }, + add: function add(value) { + this.strings.push(value); + }, + get count() { + return this.strings.length; + } + }; + return CFFStrings; +})(); + +var CFFIndex = (function() { + function CFFIndex() { + this.objects = []; + this.length = 0; + } + CFFIndex.prototype = { + add: function add(data) { + this.length += data.length; + this.objects.push(data); + }, + get: function get(index) { + return this.objects[index]; + }, + get count() { + return this.objects.length; + } + }; + return CFFIndex; +})(); + +var CFFDict = (function CFFDictClosure() { + function CFFDict(tables, strings) { + this.keyToNameMap = tables.keyToNameMap; + this.nameToKeyMap = tables.nameToKeyMap; + this.defaults = tables.defaults; + this.types = tables.types; + this.opcodes = tables.opcodes; + this.order = tables.order; + this.strings = strings; + this.values = {}; + } + CFFDict.prototype = { + // value should always be an array + setByKey: function setByKey(key, value) { + if (!(key in this.keyToNameMap)) + return false; + // ignore empty values + if (value.length === 0) + return true; + var type = this.types[key]; + // remove the array wrapping these types of values + if (type === 'num' || type === 'sid' || type === 'offset') + value = value[0]; + this.values[key] = value; + return true; + }, + hasName: function hasName(name) { + return this.nameToKeyMap[name] in this.values; + }, + getByName: function getByName(name) { + if (!(name in this.nameToKeyMap)) + error('Invalid dictionary name "' + name + '"'); + var key = this.nameToKeyMap[name]; + if (!(key in this.values)) + return this.defaults[key]; + return this.values[key]; + }, + removeByName: function removeByName(name) { + delete this.values[this.nameToKeyMap[name]]; + }, + dump: function dump(title) { + console.log('----' + title + ' Dictionary Dump------'); + for (var key in this.values) { + if (key in this.keyToNameMap) + console.log(this.keyToNameMap[key] + '(' + key + '): ' + + this.values[key]); + else + console.log('Unknown(' + key + '): ' + this.values[key]); + } + } + }; + CFFDict.createTables = function createTables(layout) { + var tables = { + keyToNameMap: {}, + nameToKeyMap: {}, + defaults: {}, + types: {}, + opcodes: {}, + order: [] + }; + for (var i = 0, ii = layout.length; i < ii; ++i) { + var entry = layout[i]; + var key = isArray(entry[0]) ? (entry[0][0] << 8) + entry[0][1] : entry[0]; + tables.keyToNameMap[key] = entry[1]; + tables.nameToKeyMap[entry[1]] = key; + tables.types[key] = entry[2]; + tables.defaults[key] = entry[3]; + tables.opcodes[key] = isArray(entry[0]) ? entry[0] : [entry[0]]; + tables.order.push(key); + } + return tables; + }; + return CFFDict; +})(); + +var CFFTopDict = (function CFFTopDictClosure() { + var layout = [ + [[12, 30], 'ROS', ['sid', 'sid', 'num'], null], + [[12, 20], 'SyntheticBase', 'num', null], + [0, 'version', 'sid', null], + [1, 'Notice', 'sid', null], + [[12, 0], 'Copyright', 'sid', null], + [2, 'FullName', 'sid', null], + [3, 'FamilyName', 'sid', null], + [4, 'Weight', 'sid', null], + [[12, 1], 'isFixedPitch', 'num', 0], + [[12, 2], 'ItalicAngle', 'num', 0], + [[12, 3], 'UnderlinePosition', 'num', -100], + [[12, 4], 'UnderlineThickness', 'num', 50], + [[12, 5], 'PaintType', 'num', 0], + [[12, 6], 'CharstringType', 'num', 2], + [[12, 7], 'FontMatrix', ['num', 'num', 'num', 'num'], + [.001, 0, 0, .001, 0, 0]], + [13, 'UniqueID', 'num', null], + [5, 'FontBBox', ['num', 'num', 'num', 'num'], [0, 0, 0, 0]], + [[12, 8], 'StrokeWidth', 'num', 0], + [14, 'XUID', 'array', null], + [15, 'charset', 'offset', 0], + [16, 'Encoding', 'offset', 0], + [17, 'CharStrings', 'offset', 0], + [18, 'Private', ['offset', 'offset'], null], + [[12, 21], 'PostScript', 'sid', null], + [[12, 22], 'BaseFontName', 'sid', null], + [[12, 23], 'BaseFontBlend', 'delta', null], + [[12, 31], 'CIDFontVersion', 'num', 0], + [[12, 32], 'CIDFontRevision', 'num', 0], + [[12, 33], 'CIDFontType', 'num', 0], + [[12, 34], 'CIDCount', 'num', 8720], + [[12, 35], 'UIDBase', 'num', null], + [[12, 36], 'FDArray', 'offset', null], + [[12, 37], 'FDSelect', 'offset', null], + [[12, 38], 'FontName', 'sid', null]]; + var tables = null; + function CFFTopDict(strings) { + if (tables === null) + tables = CFFDict.createTables(layout); + CFFDict.call(this, tables, strings); + this.privateDict = null; + } + CFFTopDict.prototype = Object.create(CFFDict.prototype); + return CFFTopDict; +})(); + +var CFFPrivateDict = (function CFFPrivateDictClosure() { + var layout = [ + [6, 'BlueValues', 'delta', null], + [7, 'OtherBlues', 'delta', null], + [8, 'FamilyBlues', 'delta', null], + [9, 'FamilyOtherBlues', 'delta', null], + [[12, 9], 'BlueScale', 'num', 0.039625], + [[12, 10], 'BlueShift', 'num', 7], + [[12, 11], 'BlueFuzz', 'num', 1], + [10, 'StdHW', 'num', null], + [11, 'StdVW', 'num', null], + [[12, 12], 'StemSnapH', 'delta', null], + [[12, 13], 'StemSnapV', 'delta', null], + [[12, 14], 'ForceBold', 'num', 0], + [[12, 17], 'LanguageGroup', 'num', 0], + [[12, 18], 'ExpansionFactor', 'num', 0.06], + [[12, 19], 'initialRandomSeed', 'num', 0], + [19, 'Subrs', 'offset', null], + [20, 'defaultWidthX', 'num', 0], + [21, 'nominalWidthX', 'num', 0] + ]; + var tables = null; + function CFFPrivateDict(strings) { + if (tables === null) + tables = CFFDict.createTables(layout); + CFFDict.call(this, tables, strings); + this.subrsIndex = null; + } + CFFPrivateDict.prototype = Object.create(CFFDict.prototype); + return CFFPrivateDict; +})(); + +var CFFCharsetPredefinedTypes = { + ISO_ADOBE: 0, + EXPERT: 1, + EXPERT_SUBSET: 2 +}; +var CFFCharsetEmbeddedTypes = { + FORMAT0: 0, + FORMAT1: 1, + FORMAT2: 2 +}; +var CFFCharset = (function CFFCharsetClosure() { + function CFFCharset(predefined, format, charset, raw) { + this.predefined = predefined; + this.format = format; + this.charset = charset; + this.raw = raw; + } + return CFFCharset; +})(); + +var CFFEncodingPredefinedTypes = { + STANDARD: 0, + EXPERT: 1 +}; +var CFFCharsetEmbeddedTypes = { + FORMAT0: 0, + FORMAT1: 1 +}; +var CFFEncoding = (function CFFEncodingClosure() { + function CFFEncoding(predefined, format, encoding, raw) { + this.predefined = predefined; + this.format = format; + this.encoding = encoding; + this.raw = raw; + } + return CFFEncoding; +})(); + +var CFFFDSelect = (function CFFFDSelectClosure() { + function CFFFDSelect(fdSelect, raw) { + this.fdSelect = fdSelect; + this.raw = raw; + } + return CFFFDSelect; +})(); + +// Helper class to keep track of where an offset is within the data and helps +// filling in that offset once it's known. +var CFFOffsetTracker = (function CFFOffsetTracker() { + function CFFOffsetTracker() { + this.offsets = {}; + } + CFFOffsetTracker.prototype = { + isTracking: function isTracking(key) { + return key in this.offsets; + }, + track: function track(key, location) { + if (key in this.offsets) + error('Already tracking location of ' + key); + this.offsets[key] = location; + }, + offset: function offset(value) { + for (var key in this.offsets) { + this.offsets[key] += value; + } + }, + setEntryLocation: function setEntryLocation(key, values, output) { + if (!(key in this.offsets)) + error('Not tracking location of ' + key); + var data = output.data; + var dataOffset = this.offsets[key]; + var size = 5; + for (var i = 0, ii = values.length; i < ii; ++i) { + var offset0 = i * size + dataOffset; + var offset1 = offset0 + 1; + var offset2 = offset0 + 2; + var offset3 = offset0 + 3; + var offset4 = offset0 + 4; + // It's easy to screw up offsets so perform this sanity check. + if (data[offset0] !== 0x1d || data[offset1] !== 0 || + data[offset2] !== 0 || data[offset3] !== 0 || data[offset4] !== 0) + error('writing to an offset that is not empty'); + var value = values[i]; + data[offset0] = 0x1d; + data[offset1] = (value >> 24) & 0xFF; + data[offset2] = (value >> 16) & 0xFF; + data[offset3] = (value >> 8) & 0xFF; + data[offset4] = value & 0xFF; + } + } + }; + return CFFOffsetTracker; +})(); + +// Takes a CFF and converts it to the binary representation. +var CFFCompiler = (function CFFCompilerClosure() { + function stringToArray(str) { + var array = []; + for (var i = 0, ii = str.length; i < ii; ++i) + array[i] = str.charCodeAt(i); + + return array; + }; + function CFFCompiler(cff) { + this.cff = cff; + } + CFFCompiler.prototype = { + compile: function compile() { + var cff = this.cff; + var output = { + data: [], + length: 0, + add: function add(data) { + this.data = this.data.concat(data); + this.length = this.data.length; + } + }; + + // Compile the five entries that must be in order. + var header = this.compileHeader(cff.header); + output.add(header); + + var nameIndex = this.compileNameIndex(cff.names); + output.add(nameIndex); + + var compiled = this.compileTopDicts([cff.topDict], output.length); + output.add(compiled.output); + var topDictTracker = compiled.trackers[0]; + + var stringIndex = this.compileStringIndex(cff.strings.strings); + output.add(stringIndex); + + var globalSubrIndex = this.compileIndex(cff.globalSubrIndex); + output.add(globalSubrIndex); + + // Now start on the other entries that have no specfic order. + if (cff.encoding && cff.topDict.hasName('Encoding')) { + if (cff.encoding.predefined) { + topDictTracker.setEntryLocation('Encoding', [cff.encoding.format], + output); + } else { + var encoding = this.compileEncoding(cff.encoding); + topDictTracker.setEntryLocation('Encoding', [output.length], output); + output.add(encoding); + } + } + + if (cff.charset && cff.topDict.hasName('charset')) { + if (cff.charset.predefined) { + topDictTracker.setEntryLocation('charset', [cff.charset.format], + output); + } else { + var charset = this.compileCharset(cff.charset); + topDictTracker.setEntryLocation('charset', [output.length], output); + output.add(charset); + } + } + + var charStrings = this.compileCharStrings(cff.charStrings); + topDictTracker.setEntryLocation('CharStrings', [output.length], output); + output.add(charStrings); + + if (cff.isCIDFont) { + var compiled = this.compileTopDicts(cff.fdArray, output.length); + topDictTracker.setEntryLocation('FDArray', [output.length], output); + output.add(compiled.output); + var fontDictTrackers = compiled.trackers; + + this.compilePrivateDicts(cff.fdArray, fontDictTrackers, output); + + topDictTracker.setEntryLocation('FDSelect', [output.length], output); + var fdSelect = this.compileFDSelect(cff.fdSelect.raw); + output.add(fdSelect); + } + + this.compilePrivateDicts([cff.topDict], [topDictTracker], output); + + return output.data; + }, + encodeNumber: function encodeNumber(value) { + if (parseFloat(value) == parseInt(value) && !isNaN(value)) // isInt + return this.encodeInteger(value); + else + return this.encodeFloat(value); + }, + encodeFloat: function encodeFloat(value) { + value = value.toString(); + // Strip off the any leading zeros. + if (value.substr(0, 2) === '0.') + value = value.substr(1); + else if (value.substr(0, 3) === '-0.') + value = '-' + value.substr(2); + var nibbles = []; + for (var i = 0, ii = value.length; i < ii; ++i) { + var a = value.charAt(i), b = value.charAt(i + 1); + var nibble; + if (a === 'e' && b === '-') { + nibble = 0xc; + ++i; + } else if (a === '.') { + nibble = 0xa; + } else if (a === 'E') { + nibble = 0xb; + } else if (a === '-') { + nibble = 0xe; + } else { + nibble = a; + } + nibbles.push(nibble); + } + nibbles.push(0xf); + if (nibbles.length % 2) + nibbles.push(0xf); + var out = [30]; + for (var i = 0, ii = nibbles.length; i < ii; i += 2) + out.push(nibbles[i] << 4 | nibbles[i + 1]); + return out; + }, + encodeInteger: function encodeInteger(value) { + var code; + if (value >= -107 && value <= 107) { + code = [value + 139]; + } else if (value >= 108 && value <= 1131) { + value = [value - 108]; + code = [(value >> 8) + 247, value & 0xFF]; + } else if (value >= -1131 && value <= -108) { + value = -value - 108; + code = [(value >> 8) + 251, value & 0xFF]; + } else if (value >= -32768 && value <= 32767) { + code = [0x1c, (value >> 8) & 0xFF, value & 0xFF]; + } else { + code = [0x1d, + (value >> 24) & 0xFF, + (value >> 16) & 0xFF, + (value >> 8) & 0xFF, + value & 0xFF]; + } + return code; + }, + compileHeader: function compileHeader(header) { + return [ + header.major, + header.minor, + header.hdrSize, + header.offSize + ]; + }, + compileNameIndex: function compileNameIndex(names) { + var nameIndex = new CFFIndex(); + for (var i = 0, ii = names.length; i < ii; ++i) + nameIndex.add(stringToArray(names[i])); + return this.compileIndex(nameIndex); + }, + compileTopDicts: function compileTopDicts(dicts, length) { + var fontDictTrackers = []; + var fdArrayIndex = new CFFIndex(); + for (var i = 0, ii = dicts.length; i < ii; ++i) { + var fontDict = dicts[i]; + var fontDictTracker = new CFFOffsetTracker(); + var fontDictData = this.compileDict(fontDict, fontDictTracker); + fontDictTrackers.push(fontDictTracker); + fdArrayIndex.add(fontDictData); + fontDictTracker.offset(length); + } + fdArrayIndex = this.compileIndex(fdArrayIndex, fontDictTrackers); + return { + trackers: fontDictTrackers, + output: fdArrayIndex + }; + }, + compilePrivateDicts: function compilePrivateDicts(dicts, trackers, output) { + for (var i = 0, ii = dicts.length; i < ii; ++i) { + var fontDict = dicts[i]; + if (!fontDict.privateDict || !fontDict.hasName('Private')) + continue; + var privateDict = fontDict.privateDict; + var privateDictTracker = new CFFOffsetTracker(); + var privateDictData = this.compileDict(privateDict, privateDictTracker); + + privateDictTracker.offset(output.length); + trackers[i].setEntryLocation('Private', + [privateDictData.length, output.length], + output); + output.add(privateDictData); + + if (privateDict.subrsIndex && privateDict.hasName('Subrs')) { + var subrs = this.compileIndex(privateDict.subrsIndex); + privateDictTracker.setEntryLocation('Subrs', [privateDictData.length], + output); + output.add(subrs); + } + } + }, + compileDict: function compileDict(dict, offsetTracker) { + var out = []; + // The dictionary keys must be in a certain order. + var order = dict.order; + for (var i = 0; i < order.length; ++i) { + var key = order[i]; + if (!(key in dict.values)) + continue; + //console.log('dict order: ' + dict.keyToNameMap[key]); + var values = dict.values[key]; + var types = dict.types[key]; + if (!isArray(types)) types = [types]; + if (!isArray(values)) values = [values]; + + // Remove any empty dict values. + if (values.length === 0) + continue; + + for (var j = 0, jj = types.length; j < jj; ++j) { + var type = types[j]; + var value = values[j]; + switch (type) { + case 'num': + case 'sid': + out = out.concat(this.encodeNumber(value)); + break; + case 'offset': + // For offsets we just insert a 32bit integer so we don't have to + // deal with figuring out the length of the offset when it gets + // replaced later on by the compiler. + var name = dict.keyToNameMap[key]; + // Some offsets have the offset and the length, so just record the + // position of the first one. + if (!offsetTracker.isTracking(name)) + offsetTracker.track(name, out.length); + out = out.concat([0x1d, 0, 0, 0, 0]); + break; + case 'array': + case 'delta': + out = out.concat(this.encodeNumber(value)); + for (var k = 1, kk = values.length; k < kk; ++k) + out = out.concat(this.encodeNumber(values[k])); + break; + default: + error('Unknown data type of ' + type); + break; + } + } + out = out.concat(dict.opcodes[key]); + } + return out; + }, + compileStringIndex: function compileStringIndex(strings) { + var stringIndex = new CFFIndex(); + for (var i = 0, ii = strings.length; i < ii; ++i) + stringIndex.add(stringToArray(strings[i])); + return this.compileIndex(stringIndex); + }, + compileGlobalSubrIndex: function compileGlobalSubrIndex() { + var globalSubrIndex = this.cff.globalSubrIndex; + this.out.writeByteArray(this.compileIndex(globalSubrIndex)); + }, + compileCharStrings: function compileCharStrings(charStrings) { + return this.compileIndex(charStrings); + }, + compileCharset: function compileCharset(charset) { + return this.compileTypedArray(charset.raw); + }, + compileEncoding: function compileEncoding(encoding) { + return this.compileTypedArray(encoding.raw); + }, + compileFDSelect: function compileFDSelect(fdSelect) { + return this.compileTypedArray(fdSelect); + }, + compileTypedArray: function compileTypedArray(data) { + var out = new Array(data.length); + for (var i = 0, ii = data.length; i < ii; ++i) + out[i] = data[i]; + return out; + }, + compileIndex: function compileIndex(index, trackers) { + trackers = trackers || []; + var objects = index.objects; + // First 2 bytes contains the number of objects contained into this index + var count = objects.length; + + // If there is no object, just create an index. This technically + // should just be [0, 0] but OTS has an issue with that. + if (count == 0) + return [0, 0, 0]; + + var data = [(count >> 8) & 0xFF, count & 0xff]; + + var lastOffset = 1; + for (var i = 0; i < count; ++i) + lastOffset += objects[i].length; + + var offsetSize; + if (lastOffset < 0x100) + offsetSize = 1; + else if (lastOffset < 0x10000) + offsetSize = 2; + else if (lastOffset < 0x1000000) + offsetSize = 3; + else + offsetSize = 4; + + // Next byte contains the offset size use to reference object in the file + data.push(offsetSize); + + // Add another offset after this one because we need a new offset + var relativeOffset = 1; + for (var i = 0; i < count + 1; i++) { + if (offsetSize === 1) { + data.push(relativeOffset & 0xFF); + } else if (offsetSize === 2) { + data.push((relativeOffset >> 8) & 0xFF, + relativeOffset & 0xFF); + } else if (offsetSize === 3) { + data.push((relativeOffset >> 16) & 0xFF, + (relativeOffset >> 8) & 0xFF, + relativeOffset & 0xFF); + } else { + data.push((relativeOffset >>> 24) & 0xFF, + (relativeOffset >> 16) & 0xFF, + (relativeOffset >> 8) & 0xFF, + relativeOffset & 0xFF); + } + + if (objects[i]) + relativeOffset += objects[i].length; + } + var offset = data.length; + + for (var i = 0; i < count; i++) { + // Notify the tracker where the object will be offset in the data. + if (trackers[i]) + trackers[i].offset(data.length); + for (var j = 0, jj = objects[i].length; j < jj; j++) + data.push(objects[i][j]); + } + return data; + } + }; + return CFFCompiler; })(); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 9460cfbec..85565f670 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -24,4 +24,5 @@ !type4psfunc.pdf !S2.pdf !zerowidthline.pdf +!issue1002.pdf !issue925.pdf diff --git a/test/pdfs/issue1002.pdf b/test/pdfs/issue1002.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b764c88414c3d4af30adfe1447a4556bc4a1c898 GIT binary patch literal 19172 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 KRdw}u;{pJPoy0u= literal 0 HcmV?d00001 diff --git a/test/test_manifest.json b/test/test_manifest.json index 23a0f8ed9..ce5bfb4aa 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -488,6 +488,13 @@ "link": true, "type": "eq" }, + { "id": "issue1002", + "file": "pdfs/issue1002.pdf", + "md5": "af62d6cd95079322d4af18edd960d15c", + "rounds": 1, + "link": false, + "type": "eq" + }, { "id": "issue1243", "file": "pdfs/issue1243.pdf", "md5": "130c849b83513d5ac5e03c6421fc7489", diff --git a/test/unit/font_spec.js b/test/unit/font_spec.js new file mode 100644 index 000000000..a0e609fe8 --- /dev/null +++ b/test/unit/font_spec.js @@ -0,0 +1,223 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +'use strict'; + +describe('font', function() { + function hexDump(bytes) { + var line = ''; + for (var i = 0, ii = bytes.length; i < ii; ++i) { + var b = bytes[i].toString(16); + if (b.length < 2) + b = '0' + b; + line += b.toString(16); + } + return line; + } + // This example font comes from the CFF spec: + // http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf + var exampleFont = '0100040100010101134142434445462b' + + '54696d65732d526f6d616e000101011f' + + 'f81b00f81c02f81d03f819041c6f000d' + + 'fb3cfb6efa7cfa1605e911b8f1120003' + + '01010813183030312e30303754696d65' + + '7320526f6d616e54696d657300000002' + + '010102030e0e7d99f92a99fb7695f773' + + '8b06f79a93fc7c8c077d99f85695f75e' + + '9908fb6e8cf87393f7108b09a70adf0b' + + 'f78e14'; + var fontData = []; + for (var i = 0; i < exampleFont.length; i += 2) { + var hex = exampleFont.substr(i, 2); + fontData.push(parseInt(hex, 16)); + } + var bytes = new Uint8Array(fontData); + fontData = {getBytes: function() { return bytes}}; + + function bytesToString(bytesArray) { + var str = ''; + for (var i = 0, ii = bytesArray.length; i < ii; i++) + str += String.fromCharCode(bytesArray[i]); + return str; + } + + describe('CFFParser', function() { + var parser = new CFFParser(fontData); + var cff = parser.parse(); + + it('parses header', function() { + var header = cff.header; + expect(header.major).toEqual(1); + expect(header.minor).toEqual(0); + expect(header.hdrSize).toEqual(4); + expect(header.offSize).toEqual(1); + }); + + it('parses name index', function() { + var names = cff.names; + expect(names.length).toEqual(1); + expect(names[0]).toEqual('ABCDEF+Times-Roman'); + }); + + it('sanitizes name index', function() { + var index = new CFFIndex(); + index.add(['['.charCodeAt(0), 'a'.charCodeAt(0)]); + + var names = parser.parseNameIndex(index); + expect(names).toEqual(['_a']); + + index = new CFFIndex(); + var longName = []; + for (var i = 0; i < 129; i++) + longName.push(0); + index.add(longName); + names = parser.parseNameIndex(index); + expect(names[0].length).toEqual(127); + }); + + it('parses string index', function() { + var strings = cff.strings; + expect(strings.count).toEqual(3); + expect(strings.get(0)).toEqual('.notdef'); + expect(strings.get(391)).toEqual('001.007'); + }); + + it('parses top dict', function() { + var topDict = cff.topDict; + // 391 version 392 FullName 393 FamilyName 389 Weight 28416 UniqueID + // -168 -218 1000 898 FontBBox 94 CharStrings 45 102 Private + expect(topDict.getByName('version')).toEqual(391); + expect(topDict.getByName('FullName')).toEqual(392); + expect(topDict.getByName('FamilyName')).toEqual(393); + expect(topDict.getByName('Weight')).toEqual(389); + expect(topDict.getByName('UniqueID')).toEqual(28416); + expect(topDict.getByName('FontBBox')).toEqual([-168, -218, 1000, 898]); + expect(topDict.getByName('CharStrings')).toEqual(94); + expect(topDict.getByName('Private')).toEqual([45, 102]); + }); + + it('parses predefined charsets', function() { + var charset = parser.parseCharsets(0, 0, null, true); + expect(charset.predefined).toEqual(true); + }); + + it('parses charset format 0', function() { + // The first three bytes make the offset large enough to skip predefined. + var bytes = new Uint8Array([0x00, 0x00, 0x00, + 0x00, // format + 0x00, 0x02 // sid/cid + ]); + parser.bytes = bytes; + var charset = parser.parseCharsets(3, 2, new CFFStrings(), false); + expect(charset.charset[1]).toEqual('exclam'); + + // CID font + var charset = parser.parseCharsets(3, 2, new CFFStrings(), true); + expect(charset.charset[1]).toEqual(2); + }); + + it('parses charset format 1', function() { + // The first three bytes make the offset large enough to skip predefined. + var bytes = new Uint8Array([0x00, 0x00, 0x00, + 0x01, // format + 0x00, 0x08, // sid/cid start + 0x01 // sid/cid left + ]); + parser.bytes = bytes; + var charset = parser.parseCharsets(3, 2, new CFFStrings(), false); + expect(charset.charset).toEqual(['.notdef', 'quoteright', 'parenleft']); + + // CID font + var charset = parser.parseCharsets(3, 2, new CFFStrings(), true); + expect(charset.charset).toEqual(['.notdef', 8, 9]); + }); + + it('parses charset format 2', function() { + // format 2 is the same as format 1 but the left is card16 + // The first three bytes make the offset large enough to skip predefined. + var bytes = new Uint8Array([0x00, 0x00, 0x00, + 0x02, // format + 0x00, 0x08, // sid/cid start + 0x00, 0x01 // sid/cid left + ]); + parser.bytes = bytes; + var charset = parser.parseCharsets(3, 2, new CFFStrings(), false); + expect(charset.charset).toEqual(['.notdef', 'quoteright', 'parenleft']); + + // CID font + var charset = parser.parseCharsets(3, 2, new CFFStrings(), true); + expect(charset.charset).toEqual(['.notdef', 8, 9]); + }); + + it('parses encoding format 0', function() { + // The first two bytes make the offset large enough to skip predefined. + var bytes = new Uint8Array([0x00, 0x00, + 0x00, // format + 0x01, // count + 0x08 // start + ]); + parser.bytes = bytes; + var encoding = parser.parseEncoding(2, {}, new CFFStrings(), null); + expect(encoding.encoding).toEqual({0x8: 1}); + }); + + it('parses encoding format 1', function() { + // The first two bytes make the offset large enough to skip predefined. + var bytes = new Uint8Array([0x00, 0x00, + 0x01, // format + 0x01, // num ranges + 0x07, // range1 start + 0x01 // range2 left + ]); + parser.bytes = bytes; + var encoding = parser.parseEncoding(2, {}, new CFFStrings(), null); + expect(encoding.encoding).toEqual({0x7: 0x01, 0x08: 0x02}); + }); + + it('parses fdselect format 0', function() { + var bytes = new Uint8Array([0x00, // format + 0x00, // gid: 0 fd: 0 + 0x01 // gid: 1 fd: 1 + ]); + parser.bytes = bytes; + var fdSelect = parser.parseFDSelect(0, 2); + expect(fdSelect.fdSelect).toEqual([0, 1]); + }); + + it('parses fdselect format 3', function() { + var bytes = new Uint8Array([0x03, // format + 0x00, 0x02, // range count + 0x00, 0x00, // first gid + 0x09, // font dict 1 id + 0x00, 0x02, // nex gid + 0x0a, // font dict 2 gid + 0x00, 0x04, // sentinel (last gid) + ]); + parser.bytes = bytes; + var fdSelect = parser.parseFDSelect(0, 2); + expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]); + }); + // TODO fdArray + }); + describe('CFFCompiler', function() { + it('encodes integers', function() { + var c = new CFFCompiler(); + // all the examples from the spec + expect(c.encodeInteger(0)).toEqual([0x8b]); + expect(c.encodeInteger(100)).toEqual([0xef]); + expect(c.encodeInteger(-100)).toEqual([0x27]); + expect(c.encodeInteger(1000)).toEqual([0xfa, 0x7c]); + expect(c.encodeInteger(-1000)).toEqual([0xfe, 0x7c]); + expect(c.encodeInteger(10000)).toEqual([0x1c, 0x27, 0x10]); + expect(c.encodeInteger(-10000)).toEqual([0x1c, 0xd8, 0xf0]); + expect(c.encodeInteger(100000)).toEqual([0x1d, 0x00, 0x01, 0x86, 0xa0]); + expect(c.encodeInteger(-100000)).toEqual([0x1d, 0xff, 0xfe, 0x79, 0x60]); + }); + it('encodes floats', function() { + var c = new CFFCompiler(); + expect(c.encodeFloat(-2.25)).toEqual([0x1e, 0xe2, 0xa2, 0x5f]); + expect(c.encodeFloat(5e-11)).toEqual([0x1e, 0x5c, 0x11, 0xff]); + }); + // TODO a lot more compiler tests + }); +}); From 386ea373a5bfc953658fb656de59a1e940080b6c Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Sat, 10 Mar 2012 19:19:00 -0800 Subject: [PATCH 02/54] Add font spec to the unit test list. --- test/unit/jsTestDriver.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/jsTestDriver.conf b/test/unit/jsTestDriver.conf index b2d3e86f1..9a26df6a4 100644 --- a/test/unit/jsTestDriver.conf +++ b/test/unit/jsTestDriver.conf @@ -25,6 +25,7 @@ load: - ../../src/bidi.js - ../../external/jpgjs/jpg.js - ../unit/obj_spec.js + - ../unit/font_spec.js - ../unit/function_spec.js - ../unit/crypto_spec.js - ../unit/stream_spec.js From cca02415324c59417c0b21f74a2f9a5f802e8289 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Sat, 10 Mar 2012 19:37:22 -0800 Subject: [PATCH 03/54] Slight rename. --- src/fonts.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index 49933cd50..4759c89a4 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -839,7 +839,7 @@ var Font = (function FontClosure() { var subtype = properties.subtype; var cff = (subtype == 'Type1C' || subtype == 'CIDFontType0C') ? - new CFF(file, properties) : new Type1Font(name, file, properties); + new CFFFont(file, properties) : new Type1Font(name, file, properties); // Wrap the CFF data inside an OTF font file data = this.convert(name, cff, properties); @@ -3384,8 +3384,8 @@ Type1Font.prototype = { } }; -var CFF = (function CFFClosure() { - function CFF(file, properties) { +var CFFFont = (function CFFFontClosure() { + function CFFFont(file, properties) { this.properties = properties; var parser = new CFFParser(file, properties); @@ -3402,7 +3402,7 @@ var CFF = (function CFFClosure() { } } - CFF.prototype = { + CFFFont.prototype = { readExtra: function readExtra(cff) { // charstrings contains info about glyphs (one element per glyph // containing mappings for {unicode, width}) @@ -3471,7 +3471,7 @@ var CFF = (function CFFClosure() { } }; - return CFF; + return CFFFont; })(); var CFFParser = (function CFFParserClosure() { @@ -3482,7 +3482,7 @@ var CFFParser = (function CFFParserClosure() { CFFParser.prototype = { parse: function parse() { var properties = this.properties; - var cff = new CFFTable(); + var cff = new CFF(); this.cff = cff; // The first five sections must be in order, all the others are reached @@ -3927,8 +3927,8 @@ var CFFParser = (function CFFParserClosure() { })(); // Compact Font Format -var CFFTable = (function CFFTableClosure() { - function CFFTable() { +var CFF = (function CFFClosure() { + function CFF() { this.header = null; this.names = []; this.topDict = null; @@ -3945,7 +3945,7 @@ var CFFTable = (function CFFTableClosure() { this.isCIDFont = false; } - return CFFTable; + return CFF; })(); var CFFHeader = (function CFFHeader() { From 1a1767ce820b62c9fa2e08c7989abed2d280dca5 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Sat, 10 Mar 2012 19:47:14 -0800 Subject: [PATCH 04/54] Fix lint. --- test/unit/font_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/font_spec.js b/test/unit/font_spec.js index a0e609fe8..9f0969324 100644 --- a/test/unit/font_spec.js +++ b/test/unit/font_spec.js @@ -191,7 +191,7 @@ describe('font', function() { 0x09, // font dict 1 id 0x00, 0x02, // nex gid 0x0a, // font dict 2 gid - 0x00, 0x04, // sentinel (last gid) + 0x00, 0x04 // sentinel (last gid) ]); parser.bytes = bytes; var fdSelect = parser.parseFDSelect(0, 2); From adee28b2ec34ad0f3982911962f272c863bc99b2 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Sun, 11 Mar 2012 18:21:38 -0700 Subject: [PATCH 05/54] Fix font matrix. --- src/fonts.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index 4759c89a4..6e130ae7e 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -4091,7 +4091,7 @@ var CFFTopDict = (function CFFTopDictClosure() { [[12, 4], 'UnderlineThickness', 'num', 50], [[12, 5], 'PaintType', 'num', 0], [[12, 6], 'CharstringType', 'num', 2], - [[12, 7], 'FontMatrix', ['num', 'num', 'num', 'num'], + [[12, 7], 'FontMatrix', ['num', 'num', 'num', 'num', 'num', 'num'], [.001, 0, 0, .001, 0, 0]], [13, 'UniqueID', 'num', null], [5, 'FontBBox', ['num', 'num', 'num', 'num'], [0, 0, 0, 0]], @@ -4456,7 +4456,6 @@ var CFFCompiler = (function CFFCompilerClosure() { var key = order[i]; if (!(key in dict.values)) continue; - //console.log('dict order: ' + dict.keyToNameMap[key]); var values = dict.values[key]; var types = dict.types[key]; if (!isArray(types)) types = [types]; From 19b670458e82b579b81ecfa17033c5073106072a Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Tue, 13 Mar 2012 18:59:16 -0500 Subject: [PATCH 06/54] Remove empty gryphs; improve glyph unicode movements --- src/fonts.js | 84 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 14 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index df0acbbc5..e3178ae9d 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1656,6 +1656,30 @@ var Font = (function FontClosure() { glyf.data = newGlyfData.subarray(0, writeOffset); } + function findEmptyGlyphs(locaTable, isGlyphLocationsLong, emptyGlyphIds) { + var itemSize, itemDecode; + if (isGlyphLocationsLong) { + itemSize = 4; + itemDecode = function fontItemDecodeLong(data, offset) { + return (data[offset] << 24) | (data[offset + 1] << 16) | + (data[offset + 2] << 8) | data[offset + 3]; + }; + } else { + itemSize = 2; + itemDecode = function fontItemDecode(data, offset) { + return (data[offset] << 9) | (data[offset + 1] << 1); + }; + } + var data = locaTable.data, length = data.length; + var lastOffset = itemDecode(data, 0); + for (var i = itemSize, j = 0; i < length; i += itemSize, j++) { + var offset = itemDecode(data, i); + if (offset == lastOffset) + emptyGlyphIds[j] = true; + lastOffset = offset; + } + } + function readGlyphNameMap(post, properties) { var start = (font.start ? font.start : 0) + post.offset; font.pos = start; @@ -1782,11 +1806,15 @@ var Font = (function FontClosure() { sanitizeMetrics(font, hhea, hmtx, numGlyphs); sanitizeMetrics(font, vhea, vmtx, numGlyphs); + var isGlyphLocationsLong = int16([head.data[50], head.data[51]]); if (head && loca && glyf) { - var isGlyphLocationsLong = int16([head.data[50], head.data[51]]); sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong); } + var emptyGlyphIds = []; + if (glyf) + findEmptyGlyphs(loca, isGlyphLocationsLong, emptyGlyphIds); + // Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth // Sometimes it's 0. That needs to be fixed if (hhea.data[10] == 0 && hhea.data[11] == 0) { @@ -1918,6 +1946,15 @@ var Font = (function FontClosure() { } } + // remove glyph references outside range of avaialable glyphs or empty + for (var i = ids.length - 1; i >= 0; i--) { + if (ids[i] < numGlyphs && + (!emptyGlyphIds[ids[i]] || this.isSymbolicFont)) + continue; + ids.splice(i, 1); + glyphs.splice(i, 1); + } + if (hasShortCmap && this.hasEncoding && !this.isSymbolicFont) { // Re-encode short map encoding to unicode -- that simplifies the // resolution of MacRoman encoded glyphs logic for TrueType fonts: @@ -1951,9 +1988,11 @@ var Font = (function FontClosure() { // Re-encode cmap encoding to unicode, based on the 'post' table data // diffrence array or base encoding var reverseMap = []; - for (var i = 0, ii = glyphs.length; i < ii; i++) + for (var i = 0, ii = glyphs.length; i < ii; i++) { reverseMap[glyphs[i].unicode] = i; + } + var backtrackReplacements = []; for (var i = 0, ii = glyphs.length; i < ii; i++) { var code = glyphs[i].unicode; var changeCode = false; @@ -1966,13 +2005,36 @@ var Font = (function FontClosure() { } if (glyphName in GlyphsUnicode) { var unicode = GlyphsUnicode[glyphName]; - if (!unicode || (unicode in reverseMap)) - continue; // unknown glyph name or its place is taken + if (!unicode || reverseMap[unicode] === i) + continue; // unknown glyph name or in its own place - glyphs[i].unicode = unicode; - reverseMap[unicode] = i; - if (changeCode) - toFontChar[code] = unicode; + if (unicode in reverseMap) { + backtrackReplacements[unicode] = { + index: i, + code: code, + changeCode: changeCode + }; + continue; // its place is taken + } + + var index = i; + while (true) { + glyphs[index].unicode = unicode; + reverseMap[unicode] = index; + if (changeCode) + toFontChar[code] = unicode; + + // checking if available place can be used by other glyph + var backtrack = backtrackReplacements[code]; + if (!backtrack) + break; + + delete backtrackReplacements[code]; + index = backtrack.index; + code = backtrack.code; + changeCode = backtrack.changeCode; + unicode = code; + } } this.useToFontChar = true; } @@ -1988,12 +2050,6 @@ var Font = (function FontClosure() { this.useToFontChar = true; } - // remove glyph references outside range of avaialable glyphs - for (var i = 0, ii = ids.length; i < ii; i++) { - if (ids[i] >= numGlyphs) - ids[i] = 0; - } - createGlyphNameMap(glyphs, ids, properties); this.glyphNameMap = properties.glyphNameMap; From c4c923bdcf0a65b26e5061aa03a507038b743963 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Fri, 16 Mar 2012 11:58:23 -0700 Subject: [PATCH 07/54] Fix string font names. --- src/evaluator.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/evaluator.js b/src/evaluator.js index e34787e41..64b5b3634 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -807,6 +807,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var firstChar = xref.fetchIfRef(dict.get('FirstChar')) || 0; var lastChar = xref.fetchIfRef(dict.get('LastChar')) || maxCharIndex; var fontName = xref.fetchIfRef(descriptor.get('FontName')); + // Some bad pdf's have a string as the font name. + if (isString(fontName)) + fontName = new Name(fontName); assertWellFormed(isName(fontName), 'invalid font name'); var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3'); From a66b1a7ad3a271daabd6d25ed6e420c476905fe0 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 17 Mar 2012 23:05:22 -0500 Subject: [PATCH 09/54] Fix unicode re-assignment; MacRomanEncoding detection --- src/fonts.js | 56 ++++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index e3178ae9d..0c26ec884 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -174,7 +174,6 @@ var Encodings = { '', '', 'Lslash', 'Oslash', 'OE', 'ordmasculine', '', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '', 'lslash', 'oslash', 'oe', 'germandbls'], WinAnsiEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', @@ -1947,12 +1946,22 @@ var Font = (function FontClosure() { } // remove glyph references outside range of avaialable glyphs or empty + var glyphsRemoved = 0; for (var i = ids.length - 1; i >= 0; i--) { if (ids[i] < numGlyphs && (!emptyGlyphIds[ids[i]] || this.isSymbolicFont)) continue; ids.splice(i, 1); glyphs.splice(i, 1); + glyphsRemoved++; + } + + // heuristics: if removed more than 2 glyphs encoding WinAnsiEncoding + // does not set properly + if (glyphsRemoved > 2) { + warn('Switching TrueType encoding to MacRomanEncoding for ' + + this.name + ' font'); + encoding = Encodings.MacRomanEncoding; } if (hasShortCmap && this.hasEncoding && !this.isSymbolicFont) { @@ -1992,7 +2001,7 @@ var Font = (function FontClosure() { reverseMap[glyphs[i].unicode] = i; } - var backtrackReplacements = []; + var newGlyphUnicodes = []; for (var i = 0, ii = glyphs.length; i < ii; i++) { var code = glyphs[i].unicode; var changeCode = false; @@ -2008,36 +2017,23 @@ var Font = (function FontClosure() { if (!unicode || reverseMap[unicode] === i) continue; // unknown glyph name or in its own place - if (unicode in reverseMap) { - backtrackReplacements[unicode] = { - index: i, - code: code, - changeCode: changeCode - }; - continue; // its place is taken - } - - var index = i; - while (true) { - glyphs[index].unicode = unicode; - reverseMap[unicode] = index; - if (changeCode) - toFontChar[code] = unicode; - - // checking if available place can be used by other glyph - var backtrack = backtrackReplacements[code]; - if (!backtrack) - break; - - delete backtrackReplacements[code]; - index = backtrack.index; - code = backtrack.code; - changeCode = backtrack.changeCode; - unicode = code; - } + newGlyphUnicodes[i] = unicode; + if (changeCode) + toFontChar[code] = unicode; + delete reverseMap[code]; } - this.useToFontChar = true; } + for (var index in newGlyphUnicodes) { + var unicode = newGlyphUnicodes[index]; + if (reverseMap[unicode]) { + // avoiding assigning to the same unicode + glyphs[index].unicode = unusedUnicode++; + continue; + } + glyphs[index].unicode = unicode; + reverseMap[unicode] = index; + } + this.useToFontChar = true; } // Moving all symbolic font glyphs into 0xF000 - 0xF0FF range. From b2b78cfeb2a07e6301d6f3238954b77c07bc7322 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 17 Mar 2012 23:13:54 -0500 Subject: [PATCH 10/54] Add tests from #1309 and #1317 --- test/test_manifest.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/test_manifest.json b/test/test_manifest.json index 16d924151..9cfe673f2 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -515,5 +515,19 @@ "pageLimit": 2, "link": true, "type": "eq" + }, + { "id": "issue1309", + "file": "pdfs/issue1309.pdf", + "md5": "e835fb7f3dab3073ad37d0bd3c6399fa", + "rounds": 1, + "link": true, + "type": "eq" + }, + { "id": "issue1317", + "file": "pdfs/issue1317.pdf", + "md5": "6fb46275b30c48c8985617d4f86199e3", + "rounds": 1, + "link": true, + "type": "eq" } ] From 73c9f8797b7c35f62f7d29f3cc77bfbd4153be46 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 17 Mar 2012 23:22:42 -0500 Subject: [PATCH 11/54] Add missing .link files; minor fix --- src/fonts.js | 3 +-- test/pdfs/issue1309.pdf.link | 1 + test/pdfs/issue1317.pdf.link | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 test/pdfs/issue1309.pdf.link create mode 100644 test/pdfs/issue1317.pdf.link diff --git a/src/fonts.js b/src/fonts.js index 0c26ec884..a13c948ab 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1997,9 +1997,8 @@ var Font = (function FontClosure() { // Re-encode cmap encoding to unicode, based on the 'post' table data // diffrence array or base encoding var reverseMap = []; - for (var i = 0, ii = glyphs.length; i < ii; i++) { + for (var i = 0, ii = glyphs.length; i < ii; i++) reverseMap[glyphs[i].unicode] = i; - } var newGlyphUnicodes = []; for (var i = 0, ii = glyphs.length; i < ii; i++) { diff --git a/test/pdfs/issue1309.pdf.link b/test/pdfs/issue1309.pdf.link new file mode 100644 index 000000000..1351d45ef --- /dev/null +++ b/test/pdfs/issue1309.pdf.link @@ -0,0 +1 @@ +http://www.lufthansa.com/mediapool/pdf/31/media_907231.pdf diff --git a/test/pdfs/issue1317.pdf.link b/test/pdfs/issue1317.pdf.link new file mode 100644 index 000000000..67c4d50ef --- /dev/null +++ b/test/pdfs/issue1317.pdf.link @@ -0,0 +1 @@ +http://iliad.fr/presse/2012/CP_080312_Free_mobile.pdf From 52222b7de3fedd24e748da34903dd15a94f92424 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 19 Mar 2012 09:09:42 -0700 Subject: [PATCH 12/54] Add test file for string font name. --- test/pdfs/.gitignore | 1 + test/pdfs/issue1350.pdf | 2184 +++++++++++++++++++++++++++++++++++++++ test/test_manifest.json | 6 + 3 files changed, 2191 insertions(+) create mode 100644 test/pdfs/issue1350.pdf diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index f14236860..4a73f264b 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -24,6 +24,7 @@ !issue1249.pdf !smaskdim.pdf !type4psfunc.pdf +!issue1350.pdf !S2.pdf !zerowidthline.pdf !issue925.pdf diff --git a/test/pdfs/issue1350.pdf b/test/pdfs/issue1350.pdf new file mode 100644 index 000000000..8b5a06aab --- /dev/null +++ b/test/pdfs/issue1350.pdf @@ -0,0 +1,2184 @@ +%PDF-1.4 +%âãÏÓ +1 0 obj +<>/Font<>>>/MediaBox[0 0 612 492]>> +endobj +2 0 obj +[/PDF/Text/ImageC] +endobj +4 0 obj +<>stream +0.000 0.000 0.000 rg +0.000 0.000 0.000 RG +q +1.000 0.000 0.000 RG + +q +561.600 0 0 254.991 25.200 501.009 cm +/I1 Do +Q + +BT +380.88 700.456 Td +/F2.0 13 Tf +<373238363438313231382d6d656d62> Tj +ET + +0.000 0.000 0.000 rg + +BT +43.2 681.008 Td +/F2.0 14 Tf +<4d6574726f526f636b> Tj +ET + +0.000 0.000 0.000 rg + +BT +43.2 661.502 Td +/F2.0 10 Tf +<5768617420796f7520676574> Tj +ET + + +BT +43.2 650.632 Td +/F1.0 10 Tf +<31206d6f6e7468206d656d626572736869702c20756e6c696d697465642072656e74616c7320616e6420626567696e6e65727320636c696d62696e6720636c617373> Tj +ET + + +BT +43.2 632.562 Td +/F2.0 10 Tf +<52656465656d61626c652061742074686520666f6c6c6f77696e67206c6f636174696f6e287329> Tj +ET + + +BT +43.2 621.692 Td +/F1.0 10 Tf +<3639204e6f726d616e2053747265657420457665726574742c204d41> Tj +ET + + +BT +43.2 596.422 Td +/F1.0 10 Tf +<20> Tj +ET + + +BT +381.6 665.92 Td +/F2.0 10 Tf +[<5075726368617365642062793a2053616d2054> 74.21875 <6f62696e2d686f63687374616474>] TJ +ET + +0.000 0.000 0.000 rg + +BT +381.6 575.92 Td +/F2.0 10 Tf +<497373756564206279204d6574726f526f636b> Tj +ET + + +BT +381.6 565.05 Td +/F2.0 10 Tf +[<497373756564206f6e20546875205365702032322031393a33303a33302055544320323031> 55.17578125 <31>] TJ +ET + + +BT +381.6 554.18 Td +/F2.0 10 Tf +<4e6f742076616c696420756e74696c3a20467269205365702032332031393a33303a333020555443> Tj +ET + + +BT +381.6 543.31 Td +/F2.0 10 Tf +[<323031> 55.17578125 <31>] TJ +ET + + +BT +381.6 532.44 Td +/F2.0 10 Tf +<50726f6d6f74696f6e616c2076616c756520657870697265733a20546875204d6172203232> Tj +ET + + +BT +381.6 521.57 Td +/F2.0 10 Tf +<30303a30303a3030202d3034303020323031322a> Tj +ET + +0.000 0.000 0.000 rg + +BT +36.0 488.064 Td +/F2.0 12 Tf +<4d6574726f526f636b20284d656d6229> Tj +ET + + +BT +36.0 467.92 Td +/F2.0 10 Tf +<496e737472756374696f6e73> Tj +ET + + +BT +36.0 449.85 Td +/F1.0 10 Tf +<2a20506c656173652063616c6c203631372d3338372d3736323520284576657265747429206f72203937382d3439392d37363235> Tj +ET + + +BT +36.0 438.98 Td +/F1.0 10 Tf +<284e657762757279706f72742920746f207265736572766520636c6173736573206f72206368616c6c656e676520636f757273652074696d652e> Tj +ET + + +BT +36.0 428.11 Td +/F1.0 10 Tf +[<466f722073687574746c6520736572766963652066726f6d207468652057> 18.06640625 <656c6c696e67746f6e20> 18.06640625 <54> 18.06640625 <2073746f702c20706c656173652063616c6c>] TJ +ET + + +BT +36.0 417.24 Td +/F1.0 10 Tf +<3631372d3338372d373632352075706f6e206172726976616c206174207468652073746174696f6e2e> Tj +ET + + +BT +36.0 406.37 Td +/F1.0 10 Tf +<2a205072696e7420766f756368657220616e64206272696e6720746f2061206d65726368616e74206c6f636174696f6e206c6973746564206f6e> Tj +ET + + +BT +36.0 395.5 Td +/F1.0 10 Tf +[<7468697320766f7563686572> 55.17578125 <2e>] TJ +ET + + +BT +36.0 384.63 Td +/F1.0 10 Tf +[<2a2057> 18.06640625 <7269746520796f7572206e616d6520616e642066756c6c206164647265737320666f756e64206f6e20796f757220494420696e20746865>] TJ +ET + + +BT +36.0 373.760000000001 Td +/F1.0 10 Tf +<73706163652070726f76696465642062656c6f77202874686520706572736f6e2072656465656d696e67207468697320766f7563686572> Tj +ET + + +BT +36.0 362.890000000001 Td +/F1.0 10 Tf +<73686f756c642070726f76696465207468697320696e666f726d6174696f6e2c206e6f74206e65636573736172696c7920746865> Tj +ET + + +BT +36.0 352.020000000001 Td +/F1.0 10 Tf +<70757263686173657220696620676976656e20617320612067696674292e20> Tj +ET + + +BT +36.0 341.150000000001 Td +/F1.0 10 Tf +<2a2050726573656e742076616c69642c20676f7665726e6d656e742d6973737565642070686f746f204944207768656e> Tj +ET + + +BT +36.0 330.280000000001 Td +/F1.0 10 Tf +<72656465656d696e6720796f757220766f75636865727320286e616d65206f6e20494420646f6573206e6f74206861766520746f> Tj +ET + + +BT +36.0 319.410000000001 Td +/F1.0 10 Tf +<6d6174636820746865207075726368617365722773206e616d6520696620676976656e20617320612067696674292e> Tj +ET + + +BT +36.0 308.540000000001 Td +ET + + +BT +36.0 297.670000000001 Td +/F1.0 10 Tf +[<496e737472756374696f6e7320666f72206d65726368616e743a2042757957> -0.0 <6974684d652068617320616c726561647920636f6c6c6563746564>] TJ +ET + + +BT +36.0 286.800000000001 Td +/F1.0 10 Tf +<7061796d656e74206f6e20796f757220626568616c662e> Tj +ET + + +BT +306.0 467.92 Td +/F2.0 10 Tf +<44657461696c73> Tj +ET + + +BT +306.0 449.85 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 438.98 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 428.11 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 417.24 Td +/F1.0 10 Tf +<6d656d6265727368697020626567696e732066726f6d20796f75722066697273742076697369743b20706c656173652070726573656e74> Tj +ET + + +BT +306.0 406.37 Td +/F1.0 10 Tf +<766f756368657220746f2061637469766174652e20> Tj +ET + + +BT +306.0 395.5 Td +/F1.0 10 Tf +[ -37.109375 18.06640625 <7320636c6173732e20> 55.17578125 <41> 55.17578125 <20636c617373206973206e6f74>] TJ +ET + + +BT +306.0 384.63 Td +/F1.0 10 Tf +[<6e656365737361727920696620796f7520616c7265616479206b6e6f7720686f7720746f2062656c6179> 74.21875 <2e>] TJ +ET + + +BT +306.0 373.760000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 362.890000000001 Td +/F1.0 10 Tf +<70756e636820636172642e> Tj +ET + + +BT +306.0 352.020000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 341.150000000001 Td +/F1.0 10 Tf +<6c6f636174696f6e206f6e6c793b206d757374206265206174206c6561737420342720362220746f20676f207468726f7567682074686520636f757273652e> Tj +ET + + +BT +306.0 330.280000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 319.410000000001 Td +/F1.0 10 Tf +[<617661696c6162696c697479> 74.21875 <2e>] TJ +ET + + +BT +306.0 308.540000000001 Td +/F1.0 10 Tf +[ 55.17578125 <4167657320313820616e6420756e646572206d7573742068617665206120776169766572207369676e6564206279206120706172656e74>] TJ +ET + + +BT +306.0 297.670000000001 Td +/F1.0 10 Tf +<6f72206c6567616c20677561726469616e2e> Tj +ET + + +BT +306.0 286.800000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 275.930000000001 Td +/F1.0 10 Tf +[<57> 18.06640625 <656c6c696e67746f6e20> 18.06640625 <54> 18.06640625 <2073746f702e>] TJ +ET + + +BT +306.0 265.060000000001 Td +/F1.0 10 Tf +[ 18.06640625 <66657273206f722070726f6d6f74696f6e732e>] TJ +ET + + +BT +306.0 254.190000000001 Td +/F1.0 10 Tf +[ 55.17578125 <6f7563686572206e6f742076616c696420756e74696c20746865206461792061667465722070757263686173652e>] TJ +ET + + +BT +306.0 243.320000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 232.450000000001 Td +/F1.0 10 Tf +<6d757374206265206163746976617465642062792030332f32322f323031322e> Tj +ET + + +BT +306.0 221.580000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 210.710000000001 Td +/F1.0 10 Tf +<30332f32322f323031322e> Tj +ET + + +BT +36.0 138.176 Td +/F2.0 8 Tf +[<4265666f72652072656465656d696e67207468697320766f7563686572> 55.17578125 <2c20706c6561736520777269746520696e207468652072657175657374656420696e666f726d6174696f6e2062656c6f77> 37.109375 <2e2020> 37.109375 <4174207468652074696d65206f6620726564656d7074696f6e2c20796f75206d7573742070726573656e7420612076616c69642c>] TJ +ET + + +BT +36.0 129.48 Td +/F2.0 8 Tf +<676f7665726e6d656e742d6973737565642070686f746f2049442074686174206d617463686573207468697320696e666f726d6174696f6e2e> Tj +ET + +0.749 0.749 0.749 RG +36.000 86.400 216.000 36.000 re +S + +BT +36.0 76.976 Td +/F1.0 8 Tf +<506c6561736520777269746520796f7572206e616d652068657265> Tj +ET + +252.000 86.400 216.000 36.000 re +S + +BT +252.0 76.976 Td +/F1.0 8 Tf +[<46756c6c20> 55.17578125 <41646472657373206173206c6973746564206f6e20796f7572204944>] TJ +ET + +468.000 86.400 108.000 36.000 re +S + +BT +468.0 76.976 Td +/F1.0 8 Tf +<5374617465206c6973746564206f6e20796f7572204944> Tj +ET + + +BT +239.576 66.176 Td +/F1.0 8 Tf +[<5468616e6b20796f7520666f72207573696e672042757957> -0.0 <6974684d652e636f6d21>] TJ +ET + + +BT +151.5 57.48 Td +/F1.0 8 Tf +<496620796f75206e65656420616e7920617373697374616e63652c20796f752063616e20726561636820757320617420696e666f40627579776974686d652e636f6d206f72203836362d3638302d37303038> Tj +ET + + +BT +67.292703125 48.784 Td +/F1.0 8 Tf +[<2a416674657220746869732065787069726174696f6e20646174652c20796f75206d6179207374696c6c2062652061626c6520746f2072656465656d207468697320766f756368657220666f7220746865207072696365206f726967696e616c6c79207061696420666f722069742c206966207265717569726564206279206c6177> 55.17578125 <2e20506c6561736520736565>] TJ +ET + + +BT +220.912 40.088 Td +/F1.0 8 Tf +<687474703a2f2f627579776974686d652e636f6d2f70616765732f66617120666f72206d6f72652064657461696c73> Tj +ET + +Q + +BT +36.0 138.176 Td +/F2.0 8 Tf +[<4265666f72652072656465656d696e67207468697320766f7563686572> 55.17578125 <2c20706c6561736520777269746520696e207468652072657175657374656420696e666f726d6174696f6e2062656c6f77> 37.109375 <2e2020> 37.109375 <4174207468652074696d65206f6620726564656d7074696f6e2c20796f75206d7573742070726573656e7420612076616c69642c>] TJ +ET + + +BT +36.0 129.48 Td +/F2.0 8 Tf +<676f7665726e6d656e742d6973737565642070686f746f2049442074686174206d617463686573207468697320696e666f726d6174696f6e2e> Tj +ET + +0.749 0.749 0.749 RG +36.000 86.400 216.000 36.000 re +S + +BT +36.0 76.976 Td +/F1.0 8 Tf +<506c6561736520777269746520796f7572206e616d652068657265> Tj +ET + +252.000 86.400 216.000 36.000 re +S + +BT +252.0 76.976 Td +/F1.0 8 Tf +[<46756c6c20> 55.17578125 <41646472657373206173206c6973746564206f6e20796f7572204944>] TJ +ET + +468.000 86.400 108.000 36.000 re +S + +BT +468.0 76.976 Td +/F1.0 8 Tf +<5374617465206c6973746564206f6e20796f7572204944> Tj +ET + + +BT +239.576 66.176 Td +/F1.0 8 Tf +[<5468616e6b20796f7520666f72207573696e672042757957> -0.0 <6974684d652e636f6d21>] TJ +ET + + +BT +151.5 57.48 Td +/F1.0 8 Tf +<496620796f75206e65656420616e7920617373697374616e63652c20796f752063616e20726561636820757320617420696e666f40627579776974686d652e636f6d206f72203836362d3638302d37303038> Tj +ET + + +BT +67.292703125 48.784 Td +/F1.0 8 Tf +[<2a416674657220746869732065787069726174696f6e20646174652c20796f75206d6179207374696c6c2062652061626c6520746f2072656465656d207468697320766f756368657220666f7220746865207072696365206f726967696e616c6c79207061696420666f722069742c206966207265717569726564206279206c6177> 55.17578125 <2e20506c6561736520736565>] TJ +ET + + +BT +220.912 40.088 Td +/F1.0 8 Tf +<687474703a2f2f627579776974686d652e636f6d2f70616765732f66617120666f72206d6f72652064657461696c73> Tj +ET + +Q + +endstream +endobj +5 0 obj +<>stream +ÿØÿàJFIFHHÿá€ExifMM*JR(‡iZHH @ îÿÛC   +  + + +   ÿÛC + +ÿÀî@"ÿÄ + ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ +%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖ×ØÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ + ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ +$4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖ×ØÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®rúúê VÆÖ ¢IèØÝ×·á]s7°_OªiÎ8´–•$Ç9•ÈôÿkŠ×[¨d0Ë+@ñ´‚Aӊʵ¹ž}bä%üRY‘’ÜŒäqÐçÖ–ÊÞÄè›­e÷¶îX’3üê®a®¯+$3HR3Ä¡T~æ?ñúëh¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠüLŠ/Ú3ã'íñ§áïÃß:îý«j÷QA{â-BÖÚh¯¼•Ž5‡~Üy¨… +vÀë?ðÊŸ·ý“á_­ÿñš?eOù>ÚKýíÿNðW¬|3øg­|`Ö¾7ëzßÆÿŠšWöWõ­ÒÃÃþ'’ÖÚ h¤FEXÙ_n<Ò(P Šô*TpvVI%Ðñ©RUÝÛmõ¶Ç“ÿÃ*~ÜôrMÿ…~·ÿÆhÿ†Tý¸?èä›ÿ +ýoÿŒ×Ö?ðËkÿGñÃÿ ÿãTÃ-¯ýOÇü,[ÿV~ßÍ}ÆßT_Êÿð#äïøeOÛƒþŽI¿ð¯ÖÿøÍ'ü2¯íÁÿG$ßøWë_üf¾±ÿ†Z_ú8ŸŽøX·ÿ£þhÑÅ|pÿÂÅ¿øÕßÍ}ÁõOî¿üù?þSöàÿ£’oü+õ¿þ3Gü2§íÁÿG$ßøXküf¾°ÿ†Z_ú8ŸŽøX·ÿ£þièâ~8ábßüjoæ¾àú§÷_þ|Ÿÿ «ûpÑÉ7þú×ÿ£þWöàÿ£’oü+õ¯þ3_XÃ-/ýOÇü,[ÿRÿÃ-¯ýOÇü,[ÿQíü×ÜTþëÿÀ“¿á•?nú9&ÿ¿[ÿã4Ã*þÜôrMÿ…~µÿÆkëøe¥ÿ£‰øáÿ…‹ñª?á–‡ýWÇü,[ÿQíü×ÜTþëÿÀ“ÿá•?nú9&ÿ¿[ÿã4Ã*~ÜôrMÿ…~·ÿÆkëøe¡ÿGñÃÿ ÿãT¿ðËkÿGñÃÿ ÿãT{5÷Õò¿üù?þWößÿ£‘oü+õ¿þ3Iÿ ©ûpÑÉ7þúßÿ¯¬á–×þŽ'ã‡þ-ÿƨÿ†[_ú8ŸŽøX·ÿ£Ûù¯¸>¨¿•ÿàGÉßðÊŸ·ý“á_­ÿñš?á•nú9&ÿ¿Zÿã5õü2ÚÿÑÄüpÿÂÅ¿øÕ'ü2Ðÿ£Šøáÿ…‹ñª=¿šûƒêŸÝøòü2§íÁÿG$ßøWëüføe_ÛƒþŽI¿ð¯Ö¿øÍ}aÿ ´?èâ¾8ábßüjøe¥ÿ£‰øáÿ…‹ñª=¿šûƒêŸÝøòü2¯íÁÿG$ßøWë_üføeOÛƒþŽI¿ð¯ÖÿøÍ}aÿ ´¿ôq??ð±oþ5Gü2ÒÿÑÄüpÿÂÅ¿øÕßÍ}ÁõOî¿üù?þSöàÿ£’oü+õ¿þ3Iÿ «ûpÑÉ7þú×ÿ¯¬á–‡ýWÇü,[ÿQÿ ´¿ôq??ð±oþ5G·ó_p}Sû¯ÿ>Oÿ†Tý¸?èä›ÿ +ýoÿŒÑÿ ©ûpÑÉ7þúßÿ¯¬?á–‡ýWÇü,[ÿRÿÃ-¯ýOÇü,[ÿQíü×ÜT_Êÿð#äïøe_ÛƒþŽI¿ð¯Ö¿øÍðÊŸ·ý“á_­ÿñšúÃþhÑÅ|pÿÂÅ¿øÕðËCþŽ+ã‡þ-ÿƨöþkîªuÿàGÉÿðÊ¿·ý“á_­ñš?á•nú9&ÿ¿Zÿã5õ‡ü2ÒÿÑÄüpÿÂÅ¿øÕðËKÿGñÃÿ ÿãT{5÷Õ?ºÿð#äÿøeOÛƒþŽI¿ð¯ÖÿøÍðÊŸ·ý“áa­ÿñšúÃþièâ~8ábßüjøe¡ÿGñÃÿ ÿãT{5÷Õ?ºÿð#äÿøeOÛƒþŽI¿ð¯ÖÿøÍðÊŸ·ý“á_­ÿñšúÃþièâ~8ábßüjøe¥ÿ£‰øáÿ…‹ñª=¿šûƒê‹ù_þ|Ÿÿ ©ûpÑÉ7þúßÿ£þSöàÿ£’oü+õ¿þ3_XÿÃ-¯ýOÇü,[ÿRÃ-ú8¯ŽøX·ÿ£Ûù¯¸>¨¿•ÿàGÉÿðÊŸ·ý“á_­ÿñš?á•nú9&ÿ¿Zÿã5õ‡ü2ÒÿÑÄüpÿÂÅ¿øÕðËCþŽ+ã‡þ-ÿƨöþkîªuÿàGÉÿðÊŸ·ý“á_­ÿñš?á•?nú9&ÿ¿[ÿã5õü2ÚÿÑÄüpÿÂÅ¿øÕ'ü2ÒÿÑÄüpÿÂÅ¿øÕßÍ}ÁõEü¯ÿ>Oÿ†Tý¸?èä›ÿ +ýoÿŒÑÿ «ûpÑÉ7þú×ÿ¯¬?á–ÇýOÇü,Oÿ£þhÑÅ|pÿÂÅ¿øÕßÍ}ÁõOî¿üù?þSöàÿ£’oü+õ¿þ3Gü2§íÁÿG$ßøWëüf¾°ÿ†Z_ú8ŸŽøX·ÿ£þièâ~8ábßüjoæ¾àú¢þWÿ'ÿÃ*~ÜôrMÿ…~·ÿÆhÿ†Tý¸?èä›ÿ +ýoÿŒ×ÕÿðËCþŽ'ã‡þGÿS¿á–×þŽ'ã‡þ-ÿƨöþkîª/åøòwü2¯íÁÿG$ßøWë_üføe_ÛƒþŽI¿ð¯ÖÿøÕ}aÿ ´?èâ¾8ábßüj—þmèâ~8ábßüjoæ¾àú§÷_þ|ÿ ©ûpÑÉ7þúßÿ£þSöàÿ£’oü+õ¿þ3_WÿÃ-ú8ŸŽøYþ5Kÿ ´?èâ¾8ábßüjoæ¾àú¢þWÿ'ÿÃ*~ÜôrMÿ…~·ÿÆhÿ†Tý¸?èä›ÿ +ýoÿŒ×ÖðËCþŽ+ã‡þ-ÿÆ©á–×þŽ'ã‡þ-ÿƨöþkîª/åøòwü2§íÁÿG$ßøWëüføeOÛƒþŽI¿ð¯ÖÿøÍ}cÿ ¶¿ôq??ð±oþ5Gü2ÚÿÑÄüpÿÂÅ¿øÕßÍ}ÁõEü¯ÿ>Nÿ†Tý¸?èä›ÿ oÿŒÑÿ ©ûpÑÉ7þúßÿ¯¬?á–—þŽ'ã‡þ-ÿƨÿ†Z_ú8ŸŽøX·ÿ£Ûù¯¸>¨¿•ÿàGÉÿðÊŸ·ý“á_­ÿñš?á•?nú9&ÿ¿[ÿã5õ‡ü2ÒÿÑÄüpÿÂÅ¿øÕðËKÿGñÃÿ ÿãT{5÷Õò¿üù?þSöàÿ£’oü+õ¿þ3Gü2¯íÁÿG$ßøWë_üf¾°ÿ†Z_ú8ŸŽøX·ÿ¥ÿ†[_ú8ŸŽøX·ÿ£Ûù¯¸>¨¿•ÿàGÉßðÊŸ·ý“á_­ÿñš?á•nú9&ÿ¿Zÿã5õ‡ü2ÒÿÑÄüpÿÂÅ¿øÕðËKÿGñÃÿ ÿãT{5÷Õò¿üù?þSöàÿ£’oü,5¿þ3Gü2§íÁÿG$ßøWëüf¾°ÿ†Z_ú8ŸŽøX·ÿ¥ÿ†[_ú8ŸŽøX·ÿ£Ûù¯¸>¨¿•ÿàGÉßðÊŸ·ý“áa­ÿñš?á•nú9&ÿ¿Zÿã5õ‡ü2Ðÿ£Šøáÿ…‹ñª?á–‡ýWÇü,[ÿQíü×ÜTþëÿÀ“ÿá•?nú9&ÿ¿[ÿã4Ã*~ÜôrMÿ…~·ÿÆkëøe¡ÿGñÃÿ ÿãTÃ-/ýOÇü,[ÿQíü×ÜTþëÿÀ“ÿá•?nú9&ÿ¿[ÿã4Ã*~ÜôrMÿ…~·ÿÆkêÆý—ÐH‘ÿÃD|oËäpnÄÓ/z—þmèâ~8ábßüjoæ¾àú¢þWÿ'Ã*~ÜôrMÿ…~·ÿÆhÿ†Tý¸?èä›ÿ +ýoÿŒ×Ö?ðËkÿGñÃÿ ÿãTŸðËCþŽ+ã‡þ-ÿƨöþkîª/åøòü2§íÁÿG$ßøWëüføeOÛƒþŽI¿ð¯ÖÿøÍ}aÿ ´¿ôq??ð±oþ5Gü2Ðÿ£Šøáÿ…‹ñª=¿šûƒê‹ù_þ|Ÿÿ «ûpÑÉ7þú×ÿ£þSöàÿ£’oü+õ¿þ3_XÿÃ-'ýGÇü,›ÿQÿ ¶¿ôq??ð±oþ5G·ó_p}Q+ÿÀ“¿á•?nú9&ÿ¿[ÿã4Ã*~ÜôrMÿ…~·ÿÆkëøeµÿ£‰øáÿ…‹ñª?á–×þŽ'ã‡þ-ÿƨöþkîª/åøòwü2§íÁÿG$ßøWëüføeOÛƒþŽI¿ð¯ÖÿøÍ}aÿ ´¿ôq??ð±oþ5Kÿ ¶¿ôq??ð±oþ5G·ó_p}Q+ÿÀ“¿á•nú9&ÿ¿Zÿã4Ã*~ÜôrMÿ…~·ÿÆkëøe¥ÿ£‰øáÿ…‹ñª?á–—þŽ'ã‡þ-ÿƨöþkîª/åøòü2§íÁÿG$ßøXküføeOÛƒþŽI¿ð¯ÖÿøÍ}aÿ ´?èâ¾8ábßüjøe¡ÿGñÃÿ ÿãT{5÷Õ?ºÿð#äÿøeOÛƒþŽI¿ð¯ÖÿøÍðÊŸ·ý“á_­ÿñšúÃþhÑÅ|pÿÂÅ¿øÕ/ü2ÚÿÑÄüpÿÂÅ¿øÕßÍ}ÁõEü¯ÿ>Nÿ†Uý¸?èä›ÿ +ýkÿŒÑÿ ©ûpÑÉ7þúßÿ¯¬á–×þŽ'ã‡þ-ÿÆ©?á–‡ýWÇü,[ÿQíü×ÜTþëÿÀ“ÿá•?nú9&ÿ¿[ÿã4Ã*~ÜôrMÿ…~·ÿÆkëøe¡ÿGñÃÿ ÿãT¿ðËkÿGñÃÿ ÿãT{5÷Õò¿üù?þWößÿ£‘oü+õ¿þ3Iÿ ©ûpÑÉ7þßÿ¯¬á–×þŽ'ã‡þ-ÿÆ©?á–—þŽ'ã‡þ-ÿƨöþkîªuÿàGÉÿðÊ¿·ý“á_­ÿñª?á•nú9&ÿ¿Zÿã5õ‡ü2Ðÿ£Šøáÿ…‹ñª?á–—þŽ'ã‡þ-ÿƨöþkîªuÿàGÉÿðÊŸ·ý“á_­ÿñš?á•?nú9&ÿ¿[ÿã5õ‡ü2Ðÿ£Šøáÿ…‹ñª?á–‡ýWÇü,[ÿQíü×ÜTþëÿÀ“ÿá•nú9&ÿ¿Zÿã4Ã*~ÜôrMÿ…~·ÿÆkëøe¡ÿGñÃÿ ÿãTÃ-ú8¯ŽøX·ÿ£Ûù¯¸>©ý×ÿ'ÿÃ*~ÜôrMÿ…~·ÿÆhÿ†Tý¸?èä›ÿ +ýoÿŒ×ÖðËCþŽ+ã‡þ-ÿƨÿ†Z_ú8ŸŽøX·ÿ£Ûù¯¸>¨¿•ÿàGÉÿðÊŸ·ý“á_­ÿñš?á•?nú9&ÿ¿[ÿã5õü2ÚÿÑÄüpÿÂÅ¿øÕ7þhÑÄüpÿÂÈÿñª=¿šûƒê‹ù_þ|¡ÿ ©ûpÑÉ7þßÿ£þWöàÿ£’oü+õ¯þ3_XÿÃ-¯ýOÇü,[ÿQÿ ¶¿ôq??ð±oþ5G·ó_p}Sû¯ÿ>Nÿ†Tý¸?èä›ÿ +ýoÿŒÑÿ ©ûpÑÉ7þúßÿ¯«ÿá–‡ýOÇü,ÿ¥ÿ†Zôq_?ð±oþ5G·ó_p}Q+ÿÀ“ÿá•?nú9&ÿ¿[ÿã4Ã*~ÜôrMÿ…~·ÿÆkëøeµÿ£‰øáÿ…‹ñªOøe¡ÿGñÃÿ ÿãT{5÷Õò¿üù?þSöàÿ£’oü+õ¿þ3Gü2¯íÁÿG$ßøWë_üf¾°ÿ†Zôq_?ð±oþ5Kÿ ¶¿ôq??ð±oþ5G·ó_p}Sû¯ÿ>Nÿ†Tý¸?èä›ÿ +ýoÿŒÑÿ «ûpÑÉ7þúßÿ¯¬?á–‡ýWÇü,[ÿRÿÃ-¯ýOÇü,[ÿQíü×ÜTþëÿÀ“¿á•?nú9&ÿ¿[ÿã4Ã*~ÜôrMÿ…~·ÿÆkëøe±ÿGñÃÿ ÿÆ©á–×þŽ'ã‡þ-ÿƨöþkîª/åøòwü2§íÁÿG$ßøWëüføeOÛƒþŽI¿ð¯ÖÿøÍ}aÿ ´?èâ¾8ábßüj—þmèâ~8ábßüjoæ¾àú¢þWÿ'Ã*þÜôrmÿ…~µÿƨÿ†Tý¸?èä›ÿ +ýoÿŒ×ÕÿðËCþŽ'ã‡þGÿRÿÃ-ú8¯ŽøX·ÿ£Ûù¯¸>©ý×ÿ'ÿÃ*þÜôrMÿ…~µÿÆhÿ†Tý¸?èä›ÿ +ýoÿŒ×ÖðËKÿGñÃÿ ÿãT¿ðËkÿGñÃÿ ÿãT{5÷Õò¿üù;þWöàÿ£’oü+õ¯þ3Gü2§íÁÿG$ßøWëüf¾±ÿ†[_ú8ŸŽøX·ÿ£þmèâ~8ábßüjoæ¾àú¢þWÿ'Ã*~ÜôrMÿ…~·ÿÆhÿ†Tý¸?èä›ÿ +ýoÿŒ×Ö?ðËkÿGñÃÿ ÿãTŸðËCþŽ+ã‡þ-ÿƨöþkîª/åøòü2§íÁÿG$ßøWëüføe_ÛƒþŽI¿ð¯ÖÿøÕ}cÿ ¶¿ôq??ð±oþ5Gü2ÚÿÑÄüpÿÂÅ¿øÕßÍ}ÁõEü¯ÿ>Nÿ†Tý¸?èä›ÿ +ýoÿŒÑÿ ©ûpÑÉ7þúßÿ¯¬á–×þŽ'ã‡þ-ÿƨÿ†[_ú8ŸŽøX·ÿ£Ûù¯¸>¨¿•ÿàGÉ¿ðÊ¿·ý“áa­ñš_øe_ÛƒþŽI¿ð¯ÖÿøÕ}aÿ ´¿ôq??ð±oþ5Kÿ ¶¿ôq??ð±oþ5G·ó_p}Sû¯ÿ>Nÿ†Uý¸?èä›ÿ +ýoÿW“KíðoöŒø/ð÷âÆ{XÆ­¤]KŸˆµ ›i­¥¿òZ9m›³å8*T©R:äŠúÇâgÃ=kàþµðC\Ñ>7üTÕN«ãýG»°ñ‰äº¶žÚYZ5TÝŸ( *T° 漟öªÿ“àý›ÞÐ?ôï=iNnnÎÍ4úU¤©Æê馺ßsõjŠ(¯<ö‚Š( Ê_ÙSþOƒö’ÿ{_ÿÓ¼õ‡ì·÷hŸû*ž!þp×Éß²¯üŸí%þ÷ˆ?ôï}aû-ýßÚ'þʧˆ?ô(k²¿_Dy˜O³ë#êº(®WÆ^)Ó¼áOøÃVŠy4ÍÎ[éã¶PÒ4q©f + œ"¹mÙ”¤¢›{#ª¢¾ðQ‚Äÿ÷‹yÿ§(?øõié?ðP?€××qÛj+â=& »í84Ižìbw R+¥àq+þ]³ÏY¶ ¿âÇï>梱´MoGñ&“§ëº¥o¨h×Ñ,ö×–’ #ž6 ¬8 ÖÍrÚÛž‚jJè(¢ŠQ^gñcân‹ðƒÁ:—Æt±TkCÚS’qîvÔW+mã ê¼ñf‘¬Zj:´RÌ׶$ñ‘%ðÊH$m=ëä_ +þÞ_ +¼k®h^ðç†|W6·­J-ìb–ÎY$e,¡›ÀÀëƒN +•q‹vßÈš¸ÊUI¤å·Ÿ§Þ}ÉE~'|7ý´><ë_<7¦j:„7öθ4«Gg-¬fBå¢Pñ¸³’@Í}ñoöøÖ¾øÿÇ^ ·øke‡îžÙnßRhÀT ’¡:ã5Õ,¶¼f ’m«ïþgŸO=ÂN›«&⓶«Êý/Ðý1¢¼ú_MÃøö7 ¡ÿl}Ì;wyw—»3Æq_ üý¼5¯Šüàk†öz|:üÆ&¼RiL¹y2 ”Ç^õÏO V¬e(­#¹Û[C8S©+9í£×oó?I袼ƒã?Æ_ üðœ1ñm®¡q¦Ë{ŠÅ¦Ä²Hd“;N”cƒžk(FU$£vΚ•#JsvKvzýòÂoÛáÆ/Ãà_Øë–ZÔö²ÝBu[xâŽa *•‘²À㯨¯ïm´ËÍFöQ¬M4²·DE˜Ÿ ÕT¥:R娬ȣˆ¥‰‡´¥$×t^¢¾‹þ +!ðJw‰aÐüXÉ4«Kö(@mÎ7úî‡ ý+ïX¤Y¢ŽUû®¡†}§V…Jö‘µÈÃâèb¯ìf¥nÞd´QEdt…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Ù”\"gæeoéV*eóÑOÞÚØý*z(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šøçâí³ð³á§‚>/øû]ð÷‹'Ѿø¡<'ªÅakjóOvË -Ã\*´_¾PK²6sòôÏøƒþ +Kð7ÃüûF_xOÇoàê×:=”6&ö)á2†ic7b0„Àø"F=2o¿kn?fÛëþËE§þŠ±¯¾+ÿÁ,ÿeïoêÿúíEžý©¾xü|mIñ ⮑¨kZ/Ûmí×ìÐYª4«s¶fØäH¡Boç$S<ûU|=ñãüM#Fñ'â>‘«k:WÛm O³Ã§º$ës‰ŽÉ ‘vªîœ•¯ƒfõ?ðLÏûüWÿ¢`ª_³ü}Á??ì@ñßþŽ· ¥~ ÁGþürñ'Ä øSÂ~:³Ô<¡_ø‚ýõ{ã’ÞÑ•dHŒwnL„¸ÚU<äŠô_ÙcöÔø[û^Ÿ†ÚŠ´Ñ៳ ³â[[X<Ó?™³Ëòn%Î<¦Îí½±žqøÿñÿ’»ûXÿÙ2ñ?þŒŠ¾¹ÿ‚¨ý¢ë¦ÿ·tú}ñöÐø]ðÏÂ_¼e®è>)¸Òþ붞Ö"°µ¶i®nnVF¶:‡Œ ”ÉrŒ0Ø5~þÛ_ +>'x#á?´ÅvÚGÄ¿„t¸5 [TžÅI\¼á.V°¿Ì¬ÍÓåôüÔý¬ÿä„ÁJÿì§h_ú'M¬oÙþM«öÿ²ßqÿ¤÷´úWàßÛÛàÿŽ¿hýOö_Ñü7ã(¼ay{e.¡ughºq{Tw‰å¤ÚB¤Ä 8Èíþ8ø÷àÿøËRð>³¦ëj¶^¿ñ¤’Ùà Dl¬Ø,±‚Ò)ó‰ ªŽ¬+ðoörÿ”ÁxÃþÆÿé=Å~—~Òßòq>+ÿ³ñOþ”%} á_Ú¿áç‹ßÁ)¦hž$ˆø§À“|B³ûUµºˆôèš5h¥Û1ÅÁ2®nR3óŒW‹|)ÿ‚’|øÁào<3áOÛhÿ ô¸õmZJÆÉ'¹…üÝ«l©váŸ÷-샧>Ÿ1| ÿ¿Ù§þ̓RÿÑÖ•ùÇû É´ÁEìG´ÿÛÊýÃø­ÿø%ðâw…>ø›ÂÞ8¹ñˆ,ôëÛ[2ÆÊKtŽ÷Pv{¤`W?0 +qص}_ñ'⿇¾Éàñž£r|_â+O Ù>8ÜEup$(ÒïuÛ6YwÇ¿šŸÛ·þOOà‡ý€!IðÕííò5D•£2MÿÛàîÇðv¯>ðÇíåðƒÅŸ´¦©û,é¾ñ„tûÛ» u ›K5ÓüËdgr$&B¤!Áò²N2|£ȹ¡ÙØÜéc×Êÿå2^7ÿ±ŸÄ?úO=~²üý¿~|~øÍâÞðߌì¼[£G{$÷zÕœVŽ-¥X¤ØñÜÈä–`W(2:ã¥zÏÄ?ÚgÀŸ u_‹F»¥kÓÜü>ð”2ÔÚÂÞY쥒xÖ8 J¥¦ÙÉW¸+óqøSÿÎÿ”‡|Vÿ¯_ÿétU÷ígÿ#ÏíÓÿdNÿÒ½N€?Cüoñ¯Â¾øFÿu{ Zo +­¥ï٬ᮊ\¼IÒ*îÌËŸŸ‚xÏ›üEý¯>|2³øó}¯èž%ž/„£Jmil-­˜Ý}½Q¡û.éÔ>Ñ ß¼¦;nã>SûKÿÉ€\ØÿúUe_~׿òÿ‚¡ÿ×?ÿè«ZúÇKÿ‚—| Õ¾x—öŠ¶ðŸWÁ:½‡®,¥°²Ïs$hêÈ‚ìÆcÀI6sÇzõ߆¿¶?Ã/Š>øâÏè>(·Ó¾*êú†£Ç¨[Z¤–³Ù¥ËÊ×An"k&Ò…É%rN?žÊ,~3ÙPÓÿôš +û×ö;ÿ’-ÿÍÿ±ûÅ?úK«PÝ¿ ?à¡¿¾*|bñwÁ/økÆ–þ+ðìz”·Wz’ZH,IÝ.Ø’§nPg¾+â_ü»àO¿|ø‹â/ +øî}â^q©é0iö/=¼Pº# •kµUbdÎ:äŠüˆýŒå ¿¿ë×Æú2JòÿÛ+þMkþ ßÿbŽ«ÿ£íèú_ñ×í ேßí ­iÚľ u®©ö;HakÓÆÍ‹±åX÷0dy˜ë‚{øWÄ¿ø(?ÁŸ…tÙ÷Ä^ñ¤þ4Õ¤Ó¢‚óO³²{%kÝžV÷{¥q0nÂvÍy_íEÿ(½¸ÿ±?Dþvµù›ûhsÿ?øcÿ_¾þpPûX|9Ò­¼guq¤ø‰“Ã8²ðâÅonÅõ —RHÇÌÜ!fm®l!ã.ý¨¿jχŸ²_„x'©šñ„¿ðQŸ‚/¾-Øx_Ã>7·—áæ‡{â Qõ;$[‹kS‰ÜÇt土º(=ȯœf¯½ÿ¾ÿ±/Ä¿úL•ù×ûÈÉûzÙ4ñþ†hö?ãü»àGÁ+/†7þ*𯎮âñׇàñœºE…”­ ´¿ufßvdõ +X{××_¾/xoá·„ü5ãnÇS¸ÓuÍWJÒ-¢°Š7•gÔ."·„¸gP<ªX‚Hà1À?Ë÷üþE¿Ø»þÉF™ýk÷Óö°ÿ’ðwþÇ¿ÿéÒÒ€:ψ¶WÂÿ‡øáãoCñDúgÂÍrÛ@Ö"±µ¶inn'Ž ­ƒN¡ãá.Pä7`š? ?mŸ…?| ðÇâ‡ôÛèÞ;ñaðv›£ij“Cz#’Mó„¸uXvÄß2³6qòú~nþÕ_òC?à¥öRôý%Ó«›ýŒÿäÙ?bÿû/-ÿ¤—TúOàïÛïàïÿhíCöaÒ|5ã(|g{ya%ýÝ¢Xy–Êí&$[“&Ò# /“Œ^ßã/ÚÁÞøðßVÓ5©uËO_øÞIí!… 6’,rÆ¥V3’ãjíÚGVø1û=Ê_¼Uÿcˆ?ôDõúWûAÿÉÖxŸþÍóÄÿúU}á¿Úóá¿Š/>YXh^&ŽOø"ãÇÖ qml«ŸÝñM‰É8®åõq^?ñgþ +Mð3àׄ~ x×ÄþñÕÖ™ñ+EþÝÒbÒì¬d–Þ#;nCÝ Y1*ð…Ç^}~Mø=ÿ!ÿÙþÍ·VÿÚùñûxÿÉÿ‚xÙ7_ýÖ€?t~4ÁC> ü ñ÷‚~x·Âþ6¼×|Qae¨ÙÍ£ÙYËqÝ9HÄ%Ò0`GÌXÄ×Õþ(è Û^²ÔnGŠüEgá›?ìèÑü»«þ[˹×,îa¸Ž0¦¿œø(ßü7ìÝíáÿèó_¸?µï1þÍöVü=ÿµèÛ¾|ZðçÄ«¿ˆÖš –¥ž ×çðæ ×ñF‚[˜QÚ®Û£Û*৯òÇÄø(¿Á/†ß +ϬªëÅ?hÆÙðã zxvüÿä¯k¯ ý¥›gìûñ¡ý<7~ò W5'j‘õ;««Ò’ò‘øKû?xkß~1øÁž- þÕ&–;¥IÌ……Üb@r9Qõé^û[ü8øcð‹â&‹áÏ…úÓÝZÜiíq¨iïv.Î.ðê7®NÒr1ž†¾gøqà½kâŸ|=ðïÃsÚA®kR<øóÄ^ñ†4m6×NÒÅòM§M3»·š8Æ0sÅ|9©~Ð:‡Å_اâ7‚µí/N´×< {¢ÅºTÞ «.Qb(pŽ¥J¶‚:‘]GübëÎøÝãÄôðâœÛÈ®:”á*UjN+žïô=*ªÃ‡¡JoÙò««oñ|ú¡ñ;þ +ñÀÞ>ñ÷„ôÿønæÇ@Õ&°Š{‹‹…’UF`žq]'Ç/Ûoâ7Âÿˆú¿ƒ´O +ørëN´²²¹I¯^q!3@²0;N0 =«ó/ö‹½)ñ¯ãšö%»øúW«þØ;>;øœgþaIçþ¼£®ˆaðíÃÜZ¯þDä«Æ¨U~Ñé$–Ú/M¼‘úÏñãߊ<'û2xãe†¥ÍâMBËM¹’Æv”[«\Þ6ãŒþ5ò—…¿h¿þÔþø½ð£ÄÞЬ­eð…õý¼ºcNòˆŠycqŒžÜçØük›oüÛÀ²úéZóŠ¾pÿ‚x•ðGÂ/‰2|/ñ§‚þ!ç&¡6†MÂYI1…&cÆ6ÜpJýɵøqð÷DýŸ§ñNàÇćÀÒ1Ôí,bŠ|É`wŸ0.ì¶Nyç½~'þÉvgˆ>>üÑõ«¯tÉõÒZÜ(d¥¼².áЀꭃÇŽµ7Ž1²ê»îtc0ÕÕJœù¥}¶Ö6ÓÈúŸFÿ‚üfµÖÿ[ðß…õ-ÌÌÚe¤3ÚÌ©ÜG3;|Àtܘ'®+Ýÿnïˆ:Žg/„~*ðôÌú?‰u›;ëc ÚÁ<™«Ì:ØŠù#þ + g§é´ìZuŒ©q ØÏ2[¢ ‘÷J»ˆàO°§üjÕ@ý‹ÿd &rdšþþ]¿ì¢NäXRŒisR«ÙßôeÎxgˆÃÕŸ2·_T¿SçÏ|@ºøqã¯øúÍÈ—AÔ"»uýdÛ2ŸPcfâ¿gm‹Öžýœî$Ð/ÔßxìC¦iòÆÜ´.ùdSè! ÿ}Wâsxöoƒ§âÔ%¤Ò¡ñ)ðåìÈ‹|HäÏ£1(sÜŠ·ã‹^)ø‡áƒþ¹ŠIÛÁv/£Ø rçPžiUbr;0M‘}2kzÐz°©ü¯_ÌäÃTž…Z?Γ_=áù”÷ËkldŒíHºôBÃòÅAi +üð'…õ=FÚMWÅZÝ¢6™¢[ȪÓá´’?!"]Ã-ƒ“À×àÇ?ÂÿxßáÓ\›‰ôh"ŠIÏW‘í–F#ÐbÒ½Sö¢ñ¥ÿ‹~+\7Ú +ǦhVŸf$B>Ê’¼’}h­båMËewù :¹u:Ê Þm/OˆúNø(WÇi¯Q·Ð|-’%(-ÍÔ‘ç®Ãp\ Øêϵ~‘þÍ¿ôÿÚÁ7:úiÙ~ Ó.>Ç©é¢O5"“hexßtn§ PzWãŽi›_|*ð‡ì÷ðëáÌzO‡´Ï³ƒ}¿PÔ®£—T‰I]òe‹Xôàf¿M?`¿ƒ/øaàøƒÇZlºf½â‹¨ç‹Iœâ[[XЬ~hس1^ `s\¸ÊtUò(Êúz†YW,O+ªêBÚ¶´OËú×±÷µ™´µãŸLQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW8ó“ûûN>™b«¶Ï´&Ömm¿N3V(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ùüý­äÚo¿û-Ÿú*ƾ>ø­ÿ(±ý—ì|Ö?ô+Úûöµÿ“iý¾»Åè´ÿÑV5ñÿÅoùEì»ÿcæ±ÿ¡^Ðé¿ìÏþ§þ ™ÿbŠÿôLKöqÿŸø'çýˆ>:ÿÑÖõwögÿSÿÌÿ±Åú& +¥û8ÿÇ×üóþÄÿèëzüæÿ‚yÉ^ý¬ÿì™xŸÿFÃ_\Á?Ô~Ñ?õÓFÿÛºùþ åÿ%{ö³ÿ²eâý }sÿ;ÿÚ'þºhÿûu@þÖ_òAÿà¥_öS´/ý¦Ö?ì‡ÿ&Õû +Ùo¸ÿÒ{ÚØý¬ÿä„ÁJ¿ì§h_ú'M¬oÙþM¯öÿ²ßqÿ¤÷´æß³Ÿü¦ Åÿö0xÿIç¯ÒïÚ[þN'Åöoþ)ÿÑé_š_³—ü¦ Æö0xÿIî+ô·ö•ÿ“‰ñgý›ÿŠ¿ô|tóÿÀßøûýš?ìØ5/ýi_œ°ßü›?ü[þÄkOý¼¯ÑÏ¿ñ÷û4Ù°j_ú:Ò¿8ÿaÏù6ø(¯ýˆÖŸûy@Ÿ·wüžŸÁûxGù­~íþØ_ñõû+ÿÙ\пô]Õ~~Ýßòz_ÿìáæ+÷oöÂÿ¯Ù_þÊæ…ÿ¢î¨âýþEÍ þÎÂãÿK¾Pø'ÿ)’ñ¿ýŒþ!ÿÒyëêýþEÍ þÎÂãÿK¾Pø'ÿ)’ñ¿ýŒþ!ÿÒyè#þ ›ÿ)ø¯ÿ^ž!ÿÒè«ïÚÏþGŸÛ§þÈ6ÿ¥z|ÿÎÿ”‡|Vÿ¯_ÿétU÷ígÿ#ÏíÓÿdNÿÒ½N€=Ëö—ÿ“¸ÿ°‡ô¦Ê¾;ý¯äÿCÿ®~ ÿÑVµö'í3ÿ&qÿ`/ÿéM•|wû^ÿÈþ +‡ÿ\üÿ¢­hó»Á_ò‹ŒÿöTtÿý%‚¾õýŽÿä‹Á3¿ì~ñOþ“jÕðW‚¿å?ÿì¨éÿúK}ëûÿÉÿ‚gØýâŸý&Õ¨åØËþR ñûþ½ÿקŒÿôd•åÿ¶Wüšßü¿þÅ[ÿGÛPì¯íGÿ(»¹ÿ±?DþvÕù›ûhÊO¾ÿ×ï„?ö…~™~Ôò‹»ŸûôOçm_™¿¶ü¤ûáýxCùÁ@fø×þ@?´Wýœ¿‡¿ô§M«_ð[ù ? +¿ìp_ý"¹ª¾5Ïöíÿg/áßý)ÓjÏüÇþHŸûÿH®hOöjûßðKïûüKÿ¤É_°wüŒŸ·¯ý“Oÿèf¿E?f¯½ÿ¾ÿ±/Ä¿úL•ùÙûÿÈÉûzÙ4ñþ†h†ÿ‚ƒÈ·ûÿÙ)Ó?­~ú~Ö?òB~Ø÷àý:ZWà_üþE¿Ø·þÉN™ýk÷Óö°ÿ’ðwþÇ¿ÿéÒÒ€?>ÿj¿ù!ŸðR¿û)z?þ’éÕÍþÆòlŸ±ý—–ÿÒKªé?j¯ù!ŸðR¿û)z?þ’éÕÍþÆòlŸ±ý—–ÿÒKªòÿÙçþRýâ¿û¼Aÿ¢'¯Ò¯ÚþN³Äÿöož'ÿÒ¨«óWöyÿ”¿x¯þÆ/è‰ëô¯ö…ÿ“¬ñGý›ç‰ÿôª*ùÓàïüŒ?²'ý›n«ÿ´+óßöñÿ’ ÿðÿ²n¿ú ­~ƒüÿÿì‡ÿfÛªÿí +üøý¼ä‚ÁÙ·ýíÿNó×f§£<Ì_Úõ‰úµEWé…Q@”¿²§üŸí%þ÷ˆ?ôïo_X~Ëwö‰ÿ²©âç |Ÿû*Éð~Ò_ïkÿúw‚¾°ý–þçíÿeSÄ?ú5Ù_¯¢<Ì'Ùõ‘õUxGí8éìíñ®IR5ðΠY˜€ò©=+Ýê•í•ž§isa¨ZCscp†9mî:J„r¬¤`‚;䋳Lôgh¸÷?•O†?ÛáO<5ñAŸG»ÖtYX-oî“!hÙm­ž1Þ½Kã‡íQñö“¸ð凉Δ–ZT¯-Ž‰á¸ä˜¼î» ·,îv’ c'¹¯èŸþgÃ?ú'^ÿÁU¿ÿZz_ü¡ÏöÂZ=…ÎAó¬ìa…óõU½¦åÏɪóÊa;Œd¬ó0O¼ ŒœWͳ¯íwû>xÇVñæ‹¥éúÜŽ˜Ú|ö×w>B¨\8‚¤`©= NUã·ÿ~êºÃø‡QøKáKmäµìºT #?PÄíäç½Dq‰©*ŠéšO,qp•YÇ¿ÏüÙüÆüHñ]ÿˆu¿xÓÅž±ây¤×$_³¬©+î +9Ä@·F95÷íµðÓź‹´ŸŒvÚUΡðçŘWV±‰¦ŽÆXí‘ sl¢°•ˆÚrFr+÷ Søà]fhçÖ<¡ÞÏKIw§Ã)X—!PRB€xQÀ«ÚÝÞᯠj—÷¶ˆ4-.ÊI¤µ†àC*±Ï €ªúû¼\c·üò#û%rN3ïg{uWÿ3ùÇ×?jˆþ9øA¢ü¹›L›Áz6Ñ5Õ•«™äŠi3µ@*¼à+õ¯¡à›syŸ´¶3Ÿø¦nOþLA^;ñcö½ñÅÿj>“Áþд Ëáu,ú-£CqRÄHí_LÁ2<¬]x×Ç_ä´‘<5k§ Þ프¹¸yIñX×$p ×8ê¬ùhI5kœhóâéÚ\öëkh¿CÀ?à  Ÿƒÿi¯ÜË5½µ·ˆôû=^1$Š›ßi‚B<ó +“î}ëèø%ß…-5]oâ¿Äa¸¶´··Ñ!t!Æé šP>‹?ZýpÖ¼áÜCwâ é:ÔHcIµ8§dBs´R@Ï8«š7‡¼?á¸%µðö‰a¦[JûÞ>Ù W|¸…€k†X¾j>ÊÚ÷=jy&'Ûóivíê-Ÿô‡_>&øâòÞ£k—pG²ª‘ 9–3Œô)"ãŠý£ÿ‚zxa4_ÙnM|ì2x–÷PÔüÅ ‡&$9FØ…}³¨ü>ð¯{>¥«x/B¼Ôg Ëuu§Ã,’™—'€&º/MÒ¬ Ò´½>ÚÓK…|¸í-¢Xãyà ž€Q[íi¨[PÃeÿWªês_·•ÏåàV¥§?ÆŸƒǨ[<Ÿð—Yáe$æá±Æs^ÇûjøbÿôŸÅ;=n·‹_u; dEݼ±¨-<6ÖVSŽ„s_Ð…¿ÃO‡Vw÷–~ðôpH$ŠxtÈãprH\ƒžr*Oü;ð'Ä;(tÿø?H×ì¢;£‡V³ŽàF}Wp8ü+_¯/h§Ë¥¬aý“û—O›[ßð±ù%ðïöñ¸ñO†4¿‚^+𾛦Åqáký)µô½$Í4V. +>På0Ä’Ó}c ÒmÀÜÊN9Kec–©\('’Z!ŒQŒ/ÃþM~¡S-”åR\ÿ–Ú§ú–²»Œ¿±_Åïy‘;ëúµòÙ\«)]Ä‘˜\€¬Š¼æ¼óöaýŠþ3iŸ¼ âo‹~Ið–€ÍªHæþÞäOwÄ1„F'Û~HÇÈ+ö¯Gд_Yÿgè:Ež›c¼Éö{÷§j€2{œVÅfñs÷¹vfË/§jnZ¸þ'ósûx =3öø¯oswo·PÙÜ*Ë(BCÚŒOµXý¥>j–7ÂvvrÍàxGHêp‚ÑYßÇl‘ºÊË÷€ +¹8'#9â¿ ýWÀ^×odÔu¯蚆¡"…{«Ûf‘€³)$ÇZÖ:„tqáó¢Ù`ˆüŸìãnž@û¾^6íöÆ+XãyT}ÝŒg•óºËâwZlÿ¦~|ý»§øMà-+Ã'ᆵ-cK·[ø†ÞXìžáaZãK>0 óuë_£¿±gíâ/Ú'Á¾,Ôü[aî—ªàŸö xïÿG[ÕßÙŸýOü3þÄ?ÿè˜*Ÿìãÿ?ðOÏû|wÿ£­èó—þ ãÿ%{ö²?õL¼Oÿ£"¯®à‡Ÿê?h÷ôý»¯‘¿àž_òW¿k?û&^'ÿÑ°××ðCÏø÷ý¢ë¦ÿ·tcö²ÿ’ÿ*ÿ²¡è6±¿d?ù6¯ØSþË}Ïþ“ÞVÏíeÿ$þ +Uÿe;BÿÑ:mcþÈòm?°Ÿý–ûý'½ 5ýœÿå0^/ÿ±ƒÄúO=~—~Òßòq^+ÿ³ñWþ”G_š?³—ü¦ Æö0xÿIî+ô»ö–ÿ“‰ñ_ý›ÿŠôzPÏÿ¿ãïöjÿ³`Ô¿ôu¥~r~Ã_òlÿðQ_û­?öò¿FþÿÇçìÓÿf¿©èëJüãý†ÿäÙÿࢾ¿ðƒÚíån~Ýßòz_ÿìáæ+÷oöÂÿ¯Ù_þÊæ…ÿ¢î«ð“öïÿ“Óøÿ`ÿ5¯Ý¯Û þ>¿eû+šþ‹º Œ4ù4/û; ý,zùCàŸü¦KÆÿö3ø‡ÿI篫ôù4/û; ý,zùCà—ü¦OÆßö3ø‡ÿDO@ÿðLïùHwÅoúõñþ—E_xþÖò<þÝ?öA´ïý+Ôëàïø&wü¤;â·ýzø‡ÿK¢¯¼¿k?ùnŸo€Úwþ•êtî?´Ïü˜Çý€¼;ÿ¥6Uñßí{ÿ ø*ýsð_þŠµ¯±?iŒÃ\Ø Ã¿úSe_~×ßòÿ‚¡¹à¿ýk@þ +ÿ”Xügÿ²£§ÿé,÷§ìyÿ$[þ ›ÿc÷Šô›V¯‚üÿ(±øÏÿeGOÿÒX+ï_ØïþH·ü7þÇïÿé.­@+þÆ_ò_¿õéã?ý%yí•ÿ&·ÿïÿ±GVÿÑöÕê±—ü¤ã÷ýzxÏÿFI^_ûeÉ­ÿÁ;ÿìQÕ¿ô}µ~ÊþÔ_ò‹ÛûôOçk_™¿¶‡ü¤ûáý~øCÿhWéŸíEÿ(¼¸ÿ±?DþvÕù™ûgÿÊO¾ÿ×÷„?œöoäûEÿÙÌxwÿJtÚ±ÿ±ÿ’ð§þÇÿÒšƒÆ¿òý¢ÿìåü;ÿ¥:mXÿ‚ØÉøSÿc‚ÿéÍiþÍ_{þ }ÿb_‰ô™+ó¯öÿ‘“öõÿ²iâ?ý ×è§ìÕ÷¿à—ßö%ø—ÿI’¿:ÿ`Ñÿ'íëÿdÓÄú #þ + ÿ"çì[ÿd£LþµûçûXÉ ø;ÿcßÿôéi__ðPoùÿbÏû%gõ¯ß_ÚÃþHOÁßûüÿ§KJüûýªÿä†ÁJÿì¥èÿúK§W7ûÿɲ~Åÿö^[ÿI.«¤ýª¿ä†ÁJÿì¥èÿúK§W7ûÿɲ~Åÿö^[ÿI.¨ËÿgŸùK÷Šÿìbñþˆž¿J¿h?ù:ÏÿÙ¾xŸÿJ¢¯Í_ÙçþRýâ¿û¼Aÿ¢'¯Ò¯Ú ÆVx£þÍóÄÿúU|éð{þFÙþÍ·UÿÚùñûxÿÉÿ‚xÙ7_ýÖ¿AþÈÁû"ÿÙ¶ê¿ûB¿=ÿoù ŸðOû' ÿ ÚСÿÁFÿäéfßû<;ÿ£Í~à~×ÿêÿfû+~ÿÚõøÿÿ“¦ý›¿ìXðïþ5ûû^ÿ«ýš?ì­ø{ùO@_²/ü‡k¿û*š¯þ“ÛWáÏí_øa_€?öR?ìšEÿ [WíïÁOù;Iÿìˆx_ÿK.ëñ öüÿ’#ÿøÿ²iþm_·Ÿ?äìçÿ²!áý+» Ð:(¢€>Uý©>ïìíÿeSÃßÎjù?ö«ÿ“àý›ÞÐ?ôï=}aûR}ÏÙßþʧ‡¿ô)«äÿÚ¯þOƒömÿ{@ÿÓ¼õÙ‡û>Œó1{KÖ'êÕQ\g¦QE~RþÊŸò|´—ûÚÿþ௬?e¿»ûDÿÙTñó†¾Oý•?äø?i/÷µÿý;Á_X~Ëwö‰ÿ²©âç vWëè3 ö}d}UEWé…Q@Q@Q@9âo€µêtP—k¾ x›W²ñˆ¾x3T׬Òíµ-K@³¸¸·H¿Õ*Hñ–PŸÂ{b»]WÞ׿²ÿ·4-;QþÌ»ŽúËíÖ±Íö;˜Á 4[Ù"† 8à œÜ¢€8Õø}àDDðF€±¦¨uÅUÓ u"roÛÅÆå¯ß÷¬›/„ ´ïÜ|@ÓþøJ×Ç“ÈóKâK}Ö;ù$p»\„ó 0['פQ@káσ¿¼â¯xGáo„tO\¬‰>±¤hv–—s,‡s‡š8ÃÌ žO&·5Ox\¸Ö®µ¯hz…αdºf¥-î ͨZ)b¶ó–Rd„b² “Ç&ºê(žÔ|+á}gAo +êÞÒï|0ÑÇ Ò.ìâ–ÔÆ„C ›Tª1´c¥cêß ~ëÑøš {ÀÔ¡ñˆk)¥[Î5aÄBè:8 áwîÛÛÜÑ@QÀ¿‚vþ¼ðe¿ÁïGàû»¥¾¸Ð£ðõ’ÙÏpg€G±¤ŒÞ·4†_ ô ?iÚÃï iº~3Üi6¶M¼ér¸*ò[* ³`Y$Zîè 2Ò> üðþ»}â áOƒ´ïÞ¬«u«ØhV]\ OïD“,aØ9'p$îïš­¬| ø'â7BѵÿƒÞÔô}·Ó,5YO^8#xÊĬTª8¯V¢€9}GÁžÖ<6<«xSG½ð‡–ÿaÝØÃ-Ÿ–„O!”¦Õ*ÀÀÅszÇÁŸƒþ!ñ¿Œuÿ…>Ôü]DÑkš†…i=äF/õeghË‚ŸÃƒòöÅzeÆÏð÷ÀWIÏü?,wÚ‚j÷I&™ ›ä*RæL¯Í2•R$9aƒÅ0ø}à?ˆv6šgü x›N¶˜\Ciâ 6 ø¡”doT•XÁ#p®ÊŠã4χ¾ч†ÓHð?‡ìG‡ ’ÛGzl*0;mª<•`*˜ŠÀÑ> |ðÔºäÞøGàÍ*mfÚ[-IôÝÎÜêòÒC9HÇ™’K+dÔõ*(Êuÿ¼Tº2x£à÷‚5tÑíRÇO]SÃÖW"ÂÝRwÆ|¸ÔŒ„\(ì+·Õ<5áÍrÆÏKÖt 6ÿL³š‹{KÛHæŠ b`Ñ:#ЀU€ÊÅoÑ@§ðÇᶷeâ-3Zø}á­CN×îVïVµ½Òm¦‹TT*ËpŒ„JáU@g€ gŠf“ð³áŽ¦é:.ƒðçÃn¥_iØØXiÐCeyÏúD1¢ŽnOïç­wôPšéÿ¾é~,—Ǻ_ÂßÙøæIWñ®‡iû»ýö7*‚BÌ:’Ù=ë¢Ô<á WS›YÔü+£Þk2ØI¥Éuc³Ig!Kfr»Œ,@-;Oq]EÄÚ|9ø{c&—5—€ü;o.›§>‘dðivèlì[­b!>H1Œ)ÀȬsàÁoYèwˆþx'UÓô(~Ë¥Úê^²¸M‡ƒåÛ£ÆDI•Sµ8¯T¢€<Ë_ø3ðƒÅšŽ¬x§áOƒµ[OŽ8m/u] +ÒêkXã9DÞ2Ȫyé]–©áí]þÍÞ‡§êN»ŽúÌ^Û$ßd¹!&p;$PN`ŒðknŠÃÒ|9áíMVMAÓ´é5;·¾¾kXà7—/€óJT ò6]²N95Åjßþ kÚ5‡†õß„~ Ô|=c<·Vº]þg=µ´òd’8š2ªîI,ÀORkÔh +×þüñU®…§ø£á‚u‹ ³i–Ú¦gsŽxȉ2£å@Šìì¼+á;S:ÖŸáÍ.ÛYûzÛ­ìãŽo²ÆIŽ0.ï)K©£'º(¢Š(å_Ú“î~ÎÿöU<=ÿ¡M_'þÕò|³oûÚþ篬?jO¹û;ÿÙTð÷þ…5|ŸûUÉð~Í¿ïhúwž»0ý=æbþ׬OÕª(¢¸ÏL(¢Šü¥ý•?äø?i/÷µÿý;Á_X~Ëwö‰ÿ²©âç |Ÿû*Éð~Ò_ïxƒÿNöõõ‡ì·÷?hû*ž!ÿС®Êý}æa>Ϭª¨¢Šã=0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(±U3£ÿVʬÕfUÆùä)\}H«4QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEò§íK÷goû*žÿЦ¯”?j¿ù>Ù·ýíÿNó×Õÿµ/Ýý¿ìªxÿBš¾Pýª¿äø?fß÷´ý;Ï]˜³èÏ3ö½b~­QEÆzaEPå/ì©ÿ'ÁûI½¯ÿéÞ +ú¿ö[û¿´Oý•OèP×ʲ§üŸí%þö¿ÿ§x+êÿÙoîþÑ?öUçìïÿeSÃßúÕòíWÿ'Áû6ÿ½áÿý;ÜWfìú3ÌÅý¯XŸ«TQEqž˜QEùKû*Éð~Ò_ïkÿúw‚¾¯ý–þïíÿeSÄú5ò‡ì©ÿ'ÁûI½¯ÿéÞ +ú¿ö[û¿´Oý•OèP×e~¾ˆó0ŸgÖGÕtQEqž˜QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEY—£ú+ÌŠ³U™OÚ#lð†>¤Uš(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùWö¤û¿³·ý•O9«äÿÚ¯þOƒömÿ{@ÿÓ¼õõ‡íI÷goû*žþsWÉÿµ_üŸìÛþöÿ§yë³ö}æbö—¬OÕª(¢¸ÏL(¢Šü¥ý•?äø?i/÷¼Aÿ§{zúÃö[û¿´Oý•Oÿ8käÿÙSþOƒö’ÿ{Äúw·¯¬?e¿»ûDÿÙTñó†»+õôG™„û>²>ª¢Š+ŒôŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  +Ì[όӟ®F*ÍVfýüi˜©9ôä:³@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@*þÔŸsöwÿ²©áïý +jù?ö«ÿ“àý›ÞÐ?ôï=}aûR}ßÙÛþʧ‡¿œÕòíWÿ'Áû6ÿ½ éÞzìÃýŸFy˜½¥ëõjŠ(®3Ó +(¢€?)eOù>ÚKýíÿNðWÕÿ²ßOÚ'þʧˆœò‡ì©ÿ'ÁûI½¯ÿéÞ +ú¿ö[éûDÿÙTñó‚»+õôG™„û>²>«¢Š+ŒôŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  +ìGœƒø°N=²*ÅWez?p¤gñb€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€>Uý©>ïìíÿeSÃßÎjù?ö«ÿ“àý›ÞÐ?ôï=}aûR}ßÙÛþʧ‡¿œÕòíWÿ'Áû6ÿ½ éÞzìÃýŸFy˜½¥ëõjŠ(®3Ó +(¢€?)eOù>ÚKýíÿNðWÕÿ²ßÝý¢ìªxƒÿB†¾Pý•?äø?i/÷¼Aÿ§{zúÃö[ûŸ´Gý•OÿèP×e~¾ˆó0ŸgÖGÕTQEqž˜QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEY”ý¢6ÏXcêEYªÌ[όӟ®F*ÍQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|«ûR}ßÙÛþʧ‡¿œÕòíWÿ'Áû6ÿ½ éÞzúÃö¤ûŸ³¿ý•OèSWÉÿµ_üŸìÛþöÿ§yë³ö}æbö—¬OÕª(¢¸ÏL(¢Šü¦ý•ä÷ÿi÷¼Aÿ§x+êÿÙoî~ÑöUUý©>ïìíÿeSÃßÎjù?öªÿ“àý›ÞÐ?ôï=}aûR}ßÙÛþʧ‡¿œÕòíWÿ'Áû6ÿ½ éÞzìÃýŸFy˜¿µëõjŠ(®3Ó +(¢€?)e_ù>ÚKëâý;Á_X~Ëwö‰ÿ²©âç |Ÿû*Éð~Ò_ïkÿúw‚¾¯ý–þïíÿeSÄú5Ù_¯¢<Ì'Ùõ‘õ]Q\g¦QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWer?ñ#Û«Y”ý¢6ÏXcêEY Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( •?jNŸ³·ý•O9ëåÚ¯þOƒömÿ{@ÿÓ¼õõ‡íI÷goû*žþsWÉÿµ_üŸìÛþöÿ§yë³ö}æbö—¬OÕª(¢¸ÏL(¢Šü¥ý•?äø?i/÷¼Aÿ§{zúÃö[û¿´Oý•Oÿ8käÿÙSþOƒö’ÿ{Äúw·¯¬?e¿»ûDÿÙTñó†»+õôG™„û>²>ª¢Š+ŒôŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  +Ì[όӟ®F*ÍVfýüi˜©9ôä:³@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@*þÔŸsöwÿ²©áïý +jù?ö«ÿ“àý›ÞÐ?ôï=}aûR}ßÙÛþʧ‡¿œÕòíWÿ'Áû6ÿ½ éÞzìÃýŸFy˜½¥ëõjŠ(®3Ó +(¢€?)eOù>ÚKýíÿNðWÕÿ²ßOÚ'þʧˆœò‡ì©ÿ'ÁûI½¯ÿéÞ +ú¿ö[éûDÿÙTñó‚»+õôG™„û>²>«¢Š+ŒôŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  +ìGœƒø°N=²*ÅWez?p¤gñb€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€>Uý©>ïìíÿeSÃßÎjù?ö«ÿ“àý›ÞÐ?ôï=}aûR}ßÙÛþʧ‡¿œÕòíWÿ'Áû6ÿ½ éÞzìÃýŸFy˜½¥ëõjŠ(®3Ó +(¢€?)eOù>ÚKýíÿNðWÕÿ²ßÝý¢ìªxƒÿB†¾Pý•?äø?i/÷¼Aÿ§{zúÃö[û¿´Oý•Oÿ8k²¿_Dy˜O³ë#êª(¢¸ÏL(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š¬Ê~Ñg€¬1õ"¬Õf-çÆ?ƒiÏ×#f€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€>Uý©>ïìíÿeSÃßÎjù?ö«ÿ“àý›ÞÐ?ôï=}aûR}ÏÙßþʧ‡¿ô)«äÿÚ¯þOƒömÿ{@ÿÓ¼õÙ‡û>Œó1{KÖ'êÕQ\g¦QE~RþÊ¿ò|´—ûÚÿþ௫ÿe¾Ÿ´Oý•Oÿ8+åÙSþOƒö’ÿ{Äúw·¯«ÿe¿»ûDÿÙTñþ… vWëè3 ö}d}WEWé…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@™¿cæ*N}9άÕv#Ï1ÉRsøŽ*ÅQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|«ûR}ßÙÛþʧ‡¿œÕòíWÿ'Áû6ÿ½ éÞzú¿ö¥û¿³·ý•OÿèSWʵ_üŸìÛþöÿ§yë³ö}æbö—¬OÕª(¢¸ÏL(¢Šü¥ý•?äø?i/÷µÿý;Á_X~Ësöˆÿ²©âý +ù?öTÿ“àý¤¿Þ×ÿôï}_û-ýßÚ'þʧˆ?ô(k²¿_Dy˜O³ë#êº(¢¸ÏL(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š€àH‡üEOUŽï>?îm9úäUš(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùWö¤ûŸ³¿ý•OèSWÉÿµ_üŸìÛþöÿ§yëêÿÚ—îþÎßöUϬªè¢Šã=0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(³g팛‘ß9Õš¬ÀyèÄüÁHÇâ9«4QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEò¯íI÷?gû*žÿЦ¯“ÿj¯ù>Ù·ýíÿNó×Öµ'Üýÿìªx{ÿBš¾Oýªÿäø?fß÷¼?ÿ§{ŠìÃôôg™‹û^±?V¨¢Šã=0¢Š(ò—öTÿ“àý¤¿ÞñþíëëÙoî~ÑöU²>ª¢Š+ŒôŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  +Ä´#碑\‘ÏéVj»ßÄù謻}rWŸÃb€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€>Uý©>çìïÿeSÃßúÕòíWÿ'Áû6ÿ½ éÞzúÃö¤û¿³·ý•O9«äÿÚ¯þOƒömÿ{Ãÿúw¸®Ì?Ùôg™‹Ú^±?V¨¢Šã=0¢Š(ò—öTÿ“àý¤¿Þ×ÿôï}_û-ýßÚ'þʧˆ?ô(kåÙWþOö‘ÿ{Äúw‚¾®ý–ú~Ñ?öUϬªè¢Šã=0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(³¾xÚF=yf«Æá?(VõäsVh¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(å_Ú“îþÎßöU<=ü毓ÿj¯ù>Ù·ýíÿNó×Öµ'Üýÿìªx{ÿBš¾Oýªÿäø?fß÷´ý;Ï]˜³èÏ3ö½b~­QEÆzaEPå/ì©ÿ'ÁûI½âý;Û×Ö²ßÝý¢ìªx‡ùÃ_'þÊ¿ò|´—ûÚÿþ௫ÿe¿»ûDÿÙTñþ… vWëè3 ö}d}WEWé…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Þ|g?.ÆüEOUØ7Ú³ò…lʬPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPÊ¿µ'Üýÿìªx{ÿBš¾Oýªÿäø?fß÷´ý;Ï_X~ÔŸsöwÿ²©áïý +jù?ö«ÿ“àý›ÞÐ?ôï=vaþÏ£<Ì^Òõ‰úµEWé…Q@”¿²§üŸí%þö¿ÿ§x+êÿÙoîþÑ?öU²>ª¢Š+ŒôŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  +Ä7Ú‚6ml~*ÍVù¾Ð˜û›N~¼b¬ÐEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPÊ¿µ'Üýÿìªx{ÿBš¾Oýª¿äø?fß÷´ý;Ï_WþÔ¿wövÿ²©áÿý +jùCö«ÿ“àý›ÞÐ?ôï=vaþÏ£<Ì_Úõ‰úµEWé…Q@”¿²§üŸí%þö¿ÿ§x+ëÙoî~ÑöUϬªè¢Šã=0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¹Ýö„#ýYSŸ®EXªÍ»í õa[?\ƒVh¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(å_Ú“îþÎßöU<=ü毓ÿj¯ù>Ù·ýíÿNó×Õÿµ/Ýý¿ìªxÿBš¾Pýª¿äø?fß®ÿ§yë³ÓÑžf/ízÄýZ¢Š+ŒôŠ( Ê_ÙSþOƒö’ÿ{_ÿÓ¼õì·÷hŸû*ž ÿС¯”?eOù>ÚKýïéÞÞ¾°ý–þçíÿeSÄ?ú5Ù_¯¢<Ì'Ùõ‘õUQ\g¦QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEVmßh@¿êö±?^1Vj»ûDk”«dþUb€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€ +(¢€>Uý©>çìïÿeSÃßúÕòíUÿ'Áû6ÿ½ éÞzúÃö¤ûŸ³¿ý•OèSWÉÿµ_üŸìÛþöÿ§yë³ÓÑžf/ízÄýZ¢Š+ŒôŠ( Ê_ÙSþOƒö’ÿ{Äúw·¯¬?e¿»ûDÿÙTñó†¾Ný•äø?i/÷¼Aÿ§x+ëÙoîþÑ?öUœŠ±UË=T¶}9UŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùWö¤û¿³·ý•O9«äÿÚ¯þOƒömÿ{@ÿÓ¼õõ‡íI÷goû*žþsWÉÿµ_üŸìÛþöÿ§yë³ö}æbö—¬OÕª(¢¸ÏL(¢Šü¥ý•?äø?i/÷µÿý;Á_WþËwö‰ÿ²©âý +ùCöTÿ“àý¤¿Þ×ÿôï}aû-ýÏÚ#þʧˆô(k²¿_Dy˜O³ë#êª(¢¸ÏL(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š¬ÌLñ¦?„œúr8üjÍVr|ø×Tœúr8«4QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEò¯íI÷goû*žþsWÉÿµ_üŸìÛþöÿ§yëëÚ“îþÎßöU<=ü毓ÿj¯ù>Ù·ýíÿNó×f§£<Ì_Úõ‰úµEWé…Q@”¿²§üŸí%þö¿ÿ§x+ëÙoîþÑ?öUÚKýíÿNðWÖ²ßÝý¢ìªx‡ùÃ]•úú#ÌÂ}ŸYUQEÆzaEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPbÇí›y*Çw§ Uš®ßëQvœ`Øàr8üjÅQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|«ûR}ßÙÛþʧ‡¿œÕòíWÿ'Áû6ÿ½ éÞzúÃö¤ûŸ³¿ý•OèSWÉÿµ_üŸìÛþ÷‡ÿôïq]˜³èÏ3ö½b~­QEÆzaEPãÁߊ^øIûaþÐÞ#øƒ¯dè×7ší”WfžãtͪFá6ÄŽÃ+œ‘Ž:äŠõ½s]ÿ‚mx[Ö|E¬ßgU»šúòàÇçO+—wØ€(Ëp WÔÚ÷ì}û:xŸ\Ö|I®|;ûNµªÝË}ysý¯¨ÇçO+—‘¶¬áFY‰À€YßðIJ÷ýü­jü‘]Žµ)>kÉ;t<¸á«Å8Ú ]½n÷>Zó?à˜ßóÙ¿ï¿RyŸðLoùìß÷߉«êoøbÙ{þ‰þVµ?þH£þƒö^ÿ¢cÿ•­Oÿ’(ö´ÿš_xþ­[ù)ýÌùgÌÿ‚cÿÏvÿ¾üMG™ÿÇÿžíÿ}øš¾¦ÿ† ý—¿è˜ÿåkSÿäŠ?áˆ?eïú&?ùZÔÿù"kOù¥÷‡Õ«%?¹Ÿ-yŸðLoùìß÷߉©<Ïø&?ü÷oûïÄÕõ7ü1ì½ÿDÇÿ+ZŸÿ$Qÿ Aû/Ñ1ÿÊÖ§ÿÉ{ZÍ/¼>­[ù)ýÌùkÌÿ‚cÏfÿ¾üMIæÁ1ÿç»ß~&¯©¿áˆ?eïú&?ùZÔÿù"øbÙ{þ‰þVµ?þH£ÚÓþi}áõjßÉOîgË>oüþ{7ý÷âj_3þ ÿ=›þûñ5}Kÿ Aû/Ñ1ÿÊÖ§ÿÉÃ~ËßôLòµ©ÿòEÖŸóKï«VþJs>Yó?à˜ÿóÝ¿ï¿RùŸðLoùìß÷߉«ê_øbÙ{þ‰þVµ?þH£þƒö^ÿ¢cÿ•­Oÿ’(ö´ÿš_x}Z·òSû™òÏ™ÿÇÿžíÿ}øš7þ ÿ=›þûñ5}Mÿ Aû/Ñ1ÿÊÖ§ÿÉÃ~ËßôLòµ©ÿòEÖŸóKï«ÖþJs>Yó?à˜ßóÙ¿ï¿QæÁ1ÿç»ß~&¯©¿áˆ?eïú&?ùZÔÿù"øbÙ{þ‰þVµ?þH£ÚÓþi}áõjßÉOîgË>oüþ{7ý÷âj<ßø&7üöoûïÄÕõ7ü1ì½ÿDÇÿ+ZŸÿ$Qÿ Aû/Ñ1ÿÊÖ§ÿÉ{ZÍ/¼>­[ù)ýÌùkÌÿ‚cÏfÿ¾üMG™ÿÆÿžÍÿ}øš¾¥ÿ† ý—¿è˜ÿåkSÿäŠ?áˆ?eïú&?ùZÔÿù"kOù¥÷‡Õ«%?¹Ÿ-yŸðLoùìß÷߉¨ó?à˜ßóÙ¿ï¿WÔ¿ðIJ÷ýü­jü‘Gü1ì½ÿDÇÿ+ZŸÿ$Qíiÿ4¾ðúµoä§÷3åŸ3þ ÿ=›þûñ5güþ{7ý÷âjú›þƒö^ÿ¢cÿ•­Oÿ’(ÿ† ý—¿è˜ÿåkSÿäŠ=­?æ—ÞV­ü”þæ|³æÿÁ1¿ç³ß~&£Ìÿ‚cÿÏvÿ¾üM_SÃ~ËßôLòµ©ÿòEðIJ÷ýü­jü‘Gµ§üÒûÃêÕ¿’ŸÜÏ–|Ïø&7üöoûïÄÔyŸðLùîß÷߉«êoøbÙ{þ‰þVµ?þH£þƒö^ÿ¢cÿ•­Oÿ’(ö´ÿš_x}Z·òSû™ò×™ÿÆÿžÍÿ}øš“Íÿ‚cÏfÿ¾üM_SÃ~ËßôLòµ©ÿòEðIJ÷ýü­jü‘Gµ§üÒûÃêÕ¿’ŸÜÏ–|Ïø&7üöoûïÄÔ¾güþ{7ý÷âjú—þƒö^ÿ¢cÿ•­Oÿ’(ÿ† ý—¿è˜ÿåkSÿäŠ=­?æ—ÞV­ü”þæ|³¿þ ÿ=Ûþûñ7øÒùŸðLoùìß÷߉«ê_øbÙ{þ‰þVµ?þH£þö^ÿ¢aÿ•­Oÿ’(ö´ÿš_x}Z·òSû™ò×™ÿÆÿžÍÿ}øš3þ ÿ=›þûñ5}Kÿ Aû/Ñ1ÿÊÖ§ÿÉÃ~ËßôLòµ©ÿòEÖŸóKï«VþJs>Zó?à˜ßóÙ¿ï¿RyŸðLoùìß÷߉«êoøbÙ{þ‰þVµ?þH£þƒö^ÿ¢cÿ•­Oÿ’(ö´ÿš_x}Z·òSû™òÏ™ÿÇÿžíÿ}øš3þ ÿ=›þûñ5}Mÿ Aû/Ñ1ÿÊÖ§ÿÉÃ~ËßôLòµ©ÿòEÖŸóKï«VþJs>Yóà˜ßóÙ¿ï¿QæÁ1ÿç»ß~&¯©¿áˆ?eïú&?ùZÔÿù"øbÙ{þ‰þVµ?þH£ÚÓþi}áõjßÉOîgË>güþ{·ý÷âj_3þ ÿ=›þûñ5}Kÿ Aû/Ñ1ÿÊÖ§ÿÉÃ~ËßôLòµ©ÿòEÖŸóKï«VþJs>Yó?à˜ÿóÝ¿ï¿QæÁ1ÿç»ß~&¯©¿áˆ?eïú&?ùZÔÿù"øbÙ{þ‰þVµ?þH£ÚÓþi}áõjßÉOîgË>oüþ{7ý÷âj<Ïø&7üöoûïÄÕõ7ü0ÿì½ÿDÃÿ+ZŸÿ$Qÿ Aû/Ñ1ÿÊÖ§ÿÉ{ZÍ/¼>­[ù)ýÌùgÌÿ‚cÏfÿ¾üMKæÁ1¿ç³ß~&¯©áˆ?eïú&?ùZÔÿù"øbÙ{þ‰þVµ?þH£ÚÓþi}áõjßÉOîgË^güþ{7ý÷âj<Ïø&7üöoûïÄÕõ/ü1ì½ÿDÇÿ+ZŸÿ$Qÿ Aû/Ñ1ÿÊÖ§ÿÉ{ZÍ/¼>­[ù)ýÌùgÌÿ‚cÏfÿ¾üMG™ÿÇÿžíÿ}øš¾¦ÿ† ý—¿è˜ÿåkSÿäŠ?áˆ?eïú&?ùZÔÿù"kOù¥÷‡Õ«%?¹Ÿ,ùŸðLùîß÷߉¨ó?à˜ßóÙ¿ï¿WÔßðIJ÷ýü­jü‘Gü1ì½ÿDÇÿ+ZŸÿ$Qíiÿ4¾ðú½oä§÷3åŸ7þ ÿ=›þûñ5oüþ{7ý÷âjú›þƒö^ÿ¢cÿ•­Oÿ’(ÿ† ý—¿è˜ÿåkSÿäŠ=­?æ—ÞV­ü”þæ|³æÿÁ1¿ç³ß~&¥ó?à˜ßóÙ¿ï¿WÔ¿ðIJ÷ýü­jü‘Gü1ì½ÿDÇÿ+ZŸÿ$Qíiÿ4¾ðúµoä§÷3åŸ3þ ÿ=›þûñ5güþ{·ý÷âjú›þƒö^ÿ¢cÿ•­Oÿ’(ÿ† ý—¿è˜ÿåkSÿäŠ=­?æ—ÞV­ü”þæ|µæÁ1¿ç³ß~&£Ìÿ‚cÏfÿ¾üM_RÿÃ~ËßôLòµ©ÿòEðIJ÷ýü­jü‘Gµ§üÒûÃêÕ¿’ŸÜÏ–¼Ïø&7üöoûïÄÔžgüþ{·ý÷âjú›þƒö^ÿ¢cÿ•­Oÿ’(ÿ† ý—¿è˜ÿåkSÿäŠ=­?æ—ÞV­ü”þæ|³æÿÁ1¿ç³ß~&¥ó?à˜ßóÙ¿ï¿WÔ¿ðIJ÷ýü­jü‘Gü1ì½ÿDÇÿ+ZŸÿ$Qíiÿ4¾ðúµoä§÷3åŸ7þ ÿ=›þûñ5güþ{·ý÷âjú›þƒö^ÿ¢cÿ•­Oÿ’(ÿ† ý—¿è˜ÿåkSÿäŠ=­?æ—ÞV­ü”þæ|³æÁ1¿ç³ß~&¥ó?à˜ßóÙ¿ï¿WÔ¿ðIJ÷ýü­jü‘Gü1ì½ÿDÇÿ+ZŸÿ$Qíiÿ4¾ðúµoä§÷3åŸ3þ ÿ=Ûþûñ5/™ÿÆÿžÍÿ}øš¾¥ÿ† ý—¿è˜ÿåkSÿäŠ?áˆ?eïú&?ùZÔÿù"kOù¥÷‡Õ«%?¹Ÿ-yŸðLoùìß÷߉©<ßø&7üöoûïÄÕõ7ü1ì½ÿDÇÿ+ZŸÿ$Qÿ Aû/Ñ1ÿÊÖ§ÿÉ{ZÍ/¼>­[ù)ýÌùkÌÿ‚cÏfÿ¾üMG™ÿÆÿžÍÿ}øš¾¥ÿ† ý—¿è˜ÿåkSÿäŠ?áˆ?eïú&?ùZÔÿù"kOù¥÷‡Õ«%?¹Ÿ,ùŸðLoùìß÷߉¨ó?à˜ÿóÝ¿ï¿WÔßðIJ÷ýü­jü‘Gü1ì½ÿDÇÿ+ZŸÿ$Qíiÿ4¾ðúµoä§÷3åŸ3þ ÿ=›þûñ5güþ{7ý÷âjú›þƒö^ÿ¢cÿ•­Oÿ’(ÿ† ý—¿è˜ÿåkSÿäŠ=­?æ—ÞW­ü”þæ|µæÁ1¿ç³ß~&£Ìÿ‚cÏfÿ¾üM_RÿÃ~ËßôLòµ©ÿòEðIJ÷ýü­jü‘Gµ§üÒûÃêÕ¿’ŸÜÏ–|ßø&7üöoûïÄÔyŸðLùîß÷߉«êoøbÙ{þ‰þVµ?þH£þƒö^ÿ¢cÿ•­Oÿ’(ö´ÿš_x}Z·òSû™òÏ™ÿÆÿžÍÿ}øš3þ ÿ=›þûñ5}Mÿ Aû/Ñ1ÿÊÖ§ÿÉÃ~ËßôLòµ©ÿòEÖŸóKï«VþJs>Yó?à˜ÿóÝ¿ï¿RùŸðLoùìß÷߉«ê_øaÿÙ{þ‰‡þVµ?þH£þƒö^ÿ¢cÿ•­Oÿ’(ö´ÿš_x}^·òSû™ò×™ÿÆÿžÍÿ}øš“üþ{·ý÷âoñ¯©¿áˆ?eïú&?ùZÔÿù"øbÙ{þ‰þVµ?þH£ÚÓþi}áõjßÉOîgË>güþ{·ý÷âj<Ïø&?ü÷oûïÄÕõ7ü1ì½ÿDÇÿ+ZŸÿ$Qÿ Aû/Ñ1ÿÊÖ§ÿÉ{ZÍ/¼>­[ù)ýÌùgÍÿ‚cÏfÿ¾üMKæÁ1¿ç³ß~&¯©áˆ?eïú&?ùZÔÿù"øbÙ{þ‰þVµ?þH£ÚÓþi}áõjßÉOîgË>oüþ{7ý÷âj_3þ ÿ=›þûñ5}Kÿ Aû/Ñ1ÿÊÖ§ÿÉÃ~ËßôLòµ©ÿòEÖŸóKï«VþJs>Yóà˜ßóÙ¿ï¿QæÁ1ÿç»ß~&¯©¿áˆ?eïú&?ùZÔÿù"øbÙ{þ‰þVµ?þH£ÚÓþi}áõjßÉOîgË^güþ{7ý÷âj<Ïø&7üöoûïÄÕõ/ü1ì½ÿDÇÿ+ZŸÿ$Qÿ Aû/Ñ1ÿÊÖ§ÿÉ{ZÍ/¼>­[ù)ýÌùgÌÿ‚cÿÏvÿ¾üMG™ÿÆÿžÍÇû~&¯©¿áˆ?eïú&?ùZÔÿù"øbÙ{þ‰þVµ?þH£ÚÓþi}áõjßÉOîgË^güþ{7ý÷âj<Ïø&7üöoûïÄÕõ/ü1ì½ÿDÇÿ+ZŸÿ$Qÿ Aû/Ñ1ÿÊÖ§ÿÉ{ZÍ/¼>­[ù)ýÌùkÌÿ‚cÏfÿ¾üMG™ÿÆÿžÍÿ}øš¾¥ÿ† ý—¿è˜ÿåkSÿäŠ?áˆ?eïú&?ùZÔÿù"kOù¥÷‡Õ«%?¹Ÿ,ùŸðLoùìß÷߉¨óà˜ßóÙ¿ï¿WÔßðIJ÷ýü­jü‘Gü1ì½ÿDÇÿ+ZŸÿ$Qíiÿ4¾ðúµoä§÷3å¯3þ ÿ=›þûñ5güþ{7ý÷âjú—þƒö^ÿ¢cÿ•­Oÿ’(ÿ† ý—¿è˜ÿåkSÿäŠ=­?æ—ÞV­ü”þæ|µæÁ1¿ç³ß~&£Ìÿ‚cÏfÿ¾üM_RÿÃ~ËßôLòµ©ÿòEðIJ÷ýü­jü‘Gµ§üÒûÃêÕ¿’ŸÜÏ–|Ïø&7üöoûïÄÔyŸðLùîß÷߉«êoøbÙ{þ‰þVµ?þH£þƒö^ÿ¢cÿ•­Oÿ’(ö´ÿš_x}Z·òSû™ò×™ÿÆÿžÍÿ}øš“Ìÿ‚cÿÏvÿ¾üM_SÃ~ËßôLòµ©ÿòEðIJ÷ýü­jü‘Gµ§üÒûÇõzßÉOîgË>güþ{·ý÷âj<Ïø&?üöoûïĵõ7ü1ì½ÿDÇÿ+ZŸÿ$Qÿ Aû/Ñ1ÿÊÖ§ÿÉ{ZÍ/¼_V­ü”þæ|³æÁ1ÿç»ß~&£Íÿ‚cÏfÿ¾üM_SÃ~ËßôLòµ©ÿòEðIJ÷ýü­jü‘Gµ§üÒûÃêÕ¿’ŸÜÏ–|Ïø&?ü÷oûïÄÔyŸðLoùìß÷߉«êoøbÙ{þ‰þVµ?þH£þƒö^ÿ¢cÿ•­Oÿ’(ö´ÿš_x}^·òSû™ò×™ÿÆÿžÍÿ}øš3þ ÿ=›þûñ5}Kÿ Aû/Ñ1ÿÊÖ§ÿÉÃ~ËßôLòµ©ÿòEÖŸóKï«VþJs>Zó?à˜ßóÙ¿ï¿QæÁ1¿ç³ß~&¯©á‡ÿeïú&ùZÔÿù"øbÙ{þ‰þVµ?þH£ÚÓþi}áõjßÉOîgÍZ&¹ÿÚðæ·£x‹F¾6úΓw õÁ#“Éž'±ÁS†à‚pEy/Æ/Š^ø¹ûaþÏ^#ø}¯khÖך”·Ö{}“.©#”Û*#,ˆr9ëkîÿøbÙ{þ‰þVµ?þH­= ö=ýœü1®hÞ$Ðþý›ZÒ®¡¾³¹þ×Ôdòg‰ÃÆÛZr§  à‚phU©Eó^MÛ¨K ^IFÐJééu±ôÝQ\g¦ÿÙ +endstream +endobj +6 0 obj +</Type/Font/Subtype/TrueType/ToUnicode 8 0 R/Widths 9 0 R/FirstChar 32/FontDescriptor 10 0 R>> +endobj +8 0 obj +<>stream +xœ]PËŽÃ ¼ó>¶‡Š´ç(Òª½äЇšÝ `R¤Æ ‡ò÷ $êJ{Àb43ÛòÜ^Zr䃽î0‚ud'?³Fèqp$Ž'0NÇ •ªG„Læn™"Ž-Yu-ä3‘Säv_Æ÷¸òÎÙÑ»Ÿs—p7‡ðÆ)B%š ÚÔèªÂM²Ø­I¼‹Ë!yþßK@8|\‡ÑÞà”FV4 ¨«ª©­m’ùGU«¡·ú¥XdÁöÍÒ¼Ìg=3§ô²q‰ÍŽðs”àCvå÷ Rk³ +endstream +endobj +9 0 obj +[277 750 750 750 750 750 750 750 333 333 389 750 277 333 277 750 556 556 556 556 556 750 556 556 556 556 333 750 750 750 750 750 750 722 722 722 722 750 610 750 750 277 750 750 750 833 722 750 666 750 722 666 610 722 750 943 750 750 750 750 750 750 750 750 750 556 610 556 610 556 333 610 610 277 750 556 277 889 610 610 610 610 389 556 333 610 556 777 556 556 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750] +endobj +10 0 obj +</ItalicAngle 0.0>> +endobj +11 0 obj +<>stream +xœì½ x•ÕÕ(¼÷~ç÷Ìóá¼''9'É!É !Ɉ0'ÚH" !´ +82h­ŠSKœÀ©å&¨Ÿq¨C½½RG´õ“¶X‡O*íG)*9ù×Þç„¡µ½÷ûïóßç¿ÏsOØã»ÖÖZ{íµö~Ïa„ #õ®hk]zÄþÍ,¨ù-„q«[¯lû÷ÁËÂN„œ —¯ºj™ù¦3¹¹V!T½¢³cm·%®EhÁ»Ç’+[;—¯¾á„®{gW[çŸg|êF¨ñ„lo_Ùº¡Sú„_‡ÐÊ€×Vu,iímÙ ý­ì„r× +€}£#Zy ÊÙË:—_9(­x ¡+d„„+–¬ëÖ¾›ôðGuv#$]Z?·¨ÄÖ|ùn„Öø–Wvoøæ>WBŠ¡|ѹ dGñ}w‘¥ò¯²šÏC¨Ì éË|çß~{fØŠdÀ* >ù™Í½ 1GñQ&ÄÉ'²@‰C˜WX}A +ŒêÎb—¡)Zÿeª^DVbù4:f¾œå3h;|5ËXýÅ,ŸMÇÌÏfù|:¾™å  £ƒÌÆÏw°|²/Fgc²þ–gsáïMÑÓDžàLÑÓ„çKu)š@½Ü›‚‘Q±aF +FF^ÃÕ)ZG +†C†—R0cô§` ÞX›‚QÐvãOS0 +šjÊMÁ@½éÊ œ¦¿¥`x¤š )þòÈov¥øË#·¹(Å_ÙÍSSü\óÂyä5_‘jŸÖßâ/mgWŠ¿<Ê4ò—¼•â/ÒÍHñ—ÂŒ¤ø }Y2Rü…v,E)þòH³LOñ—Ö·§øKëoHñÆly8Å_ÚÎ`Š¿´þÍa̖ߥø ã±|“â/ÀXM)þBûÖPŠ¿4QŠ¿4¿0Å_Àµ®A PêBkQ;ê@«‘k´tÞXˆKІm]kÛ;Vke…Åc K6ük`ô8Ô”@¹ƒÜ<´ 54“Áv ntêd5S Ôy·B}²µBxRVÁŸ†æ@ÝrÀh© RÚó:ˆ—¢Âǵ’ââqÚ¼mÚÌŽÕÝWu¶iS:º:;ºZ»a¸…ZõªUÚœöå+º×jsÚÖ¶u­k[Zø¿oxÕÕSªkæF¼ [¥M†FVÁÙÐñÙúê®öÖUçËiÒ¶[!t³q,…¶¯„´ ]uhÙÿ‹9ÐVW³“xó¡Ô%:j Í…\++%{^ µE¬µ½‚ÍQCK ÔO»Ùh)t!›ˆÖ¾VkÕº»Z—¶]ÙÚu…Ö±ìŸrHk_­uóù«Û»Û–js»[»ÛyõÒ¢Ž.­žtiK:zVwwµ·­-ü?—,£\ŸÜ±j)šuËáY[ª½lè/`Ä´ŸZVßÍmåžç^á^€xÿY™Ålg˜—@ºÒuð”Öô ‹ÙlÖB_üè<ûêFë×&Ûø,7Ñrë¹5@=m½-NÑ㪠zìàü$~"?…Ççuþ"¾Ž¯Ž?9ò:Ú& 5ulŽ0ŽlC¿çBÀŸK Ý¥Œ‡Îå‚~þîÙèè›]5Æå« íaóIr+) Ëب»Y]ÄKY}'Ì1‰‘ä™5]ŒÓÝ)n­f\M–[YKŒ—W²Œò1kcT6VÁ³% ktIŒÑqtKû£óîŒî³R¶ž{ Äß?‡d™Â.Þz˜„/eêçï)A1V±\.ÀçAJ×ÂâÔ¸¿¿íÕÿ s?×úRÖÒr¨ëb’ž”ù%gWÔ÷ÍàüUvá¸&žÇ#:“ä\ºY£k•¶ŸœëR¨YÏfÞÁÖÿ¿’„Ö ¸Þ–Z³¿r)U»®‡aÒÑ®Kqy´ +¹ + þ• 6uôhW¶^¥õ¬måÊpYÇên­»C[Ú¾¶s<ý¦uvµCåxÒiëZ­³­ëÊönª_Åtâªö%m«ið€¶ÑÅj;»:–ö,馊sýŠö%+ÎëÒöÕKVõ,…MNDÇêUWi¹íyZÛ•‹¡íó WÿËÞøÒöÕ˵®¶µ —Pu}®ƒ¤âNµ5‘Í(·zén»’êö®vèuiÇúÕ«:Z—^H„ÖäÔA³ŸUñ=Ý=ÝÚÒ¶u0e +³¢mUç…*ü¿Ëÿÿ.ÿÿ3–ÿÕìøg¶ÑÙ|ù{¸ L£FÄ„j¼SªÏd3¦òº–QÝ(otl’Ƶa&œïh¹3Û—tu¬íXÖ÷ÿÕ¾‡óÎæjÖŸ•ÅzÀYv–sˆZæ̨¡=ÍJ­º¦Ö¢1P×À¨˜”Ýv&-s!îa²˜§c«@ãQÉYNÍ£QMWëzª#ë—-£ª«@›Ó±4ã,PÌ«Z׎ÑZ©îloÕæ¶ö¬^ +TÓÆVŒ/ùÿÏt¾=çø|ÁÓ íÖ󟜳aϯMÚ³çפlÛóë.°s¿OT¾oíÿ0‚´…Ï:jA^8– mäïƒ?g/Ÿÿ4i;Ÿ_s¡}þ“ﳩÿÙŒ.èå{míó1ç}/Íkÿaçìñ kÏÚæçןµÓ/¤í¨Íþ?Ç‹:§3JqD0;Dè鎄&?MpB”H•î@Ÿà*ñ Œ|²($÷#DZy£ÖS•Ã•³¬'+gW¢*È[Ï@4¶8h Úr ˆGg4nèŒ. ï€¾Cô\ñŠÄl²BxYQ­nεì刬`¤X‘]~gÑãNˆ¹SW•ÿ4Þ¯ñÅ<áÈÝý¶G¯ =6>yÜzUUY+­ÐnÆ¡0)³:Æ•—ârÚ=nÒöâ½½KÜ8´mùEe¡ÄìÏð_¾ÀALŽ>ŸøubáŸI%cï~þ.ïüžtYôƒ¡%–û§ñS¼S|Sür¶7Û—íçÜa~¿Õû@Úéd<‘þD†lGÖ -clƺŒ3vf¼Ÿ!gP¾¸®X±-TÔ•õÐy°Ÿ`£e/ÐCc‘‘)ïŒ{‚rÄíÆõ0dÀrĺžø2Gx’q°²r&ÕbÃÑ5Ç@G›×TÚì°’K£ÍS~Ð8ˆ2F†últ }–èfk/[+Ù©­"Ê>MûE2e^£nPÒ|i$ÍAÏã4ÿš›¨dÔÍn|¥Eé2FŽŽ?¾ ¯MmÁqvÐ ãÊbáKθìÒ7¬JIäE‰7ž‰X{¿ú·è„¶¦Ærâs–_ýðôÅ3K§.vc!ñÝXùÍþª…ó/o[ùÃôÏùåÏ—ô/®>Ù¦\š k% ¸”‡>ÔK¶¸Þp‘¦ß’Nöp {¹CÂAçGÞ}²Û‰äþ‘‡Uâ±ÇáLV£:€³uc½ ë¦&b2a÷&º%à(r%¯cOšÚxÁ+Èȧªù=SÜ8<0º­G6vvö^£Ò‘úlœíºxÖã#È—v1L-'@[EQsŠ!4¢Å5Ç1%fEŠ¤”ª@T jv䰵Ũ'•»Ï’q)…•;‘"ÊÊž‰­¦®Ù ×wÍWèÚÐ8}Ú2Cb8íÊ—¯zëÚåïlÜ•øãÛ¯%¾Å7W¬¾±så5®O¹ö…3—¶Œ¹i÷e7®ÚúâÚ´çnz1qâSXO@\¾èª"úD¯0j¦ +Åè3FsWo›°È»ù>×4Ít™i¯éÓ«&E“$¨“„ŒF“iÿ\÷s¼“%JŒ¼‰3^E’n2†Â³8É`¢<}ñ<  Üø´°6<Ê»UÚ-½ q’ßRE6B|æCø<­êckÀ\˜ k›.ì*0S†›+) 팆öŠ-Ba”¿ÖúŠÅbÝãEÆ™Æ_?6 +()´@Þ(Ø2e¸ÔVê +Ù° “Ñk¾:x0q"±GNqŸ¹üo‰I&þk‚ÞH.‰ËÊxP=¯O\iè‘·È»|{…½òãæ'ƒæƒ¶çC¶·&—0ÎVc½Ú}€¼m=씞EoÑ S,yíÖ4 –•¦L¦´=S X$A*rÁ=U +Ö•ÃʈÂ)¸¾Æ ‚A=+À êLþ\×úÌ#õFlôçxØ}Ù§µO&—üÉæS [IýMɃR’ÇB˜ Ò¸Ò; PçV&vž7‘·$N¨ó¦4ýÐÚþ@ü»Äé·þ=ñ{œÿ§½¿~pãìY+:çÍîäçfÎkè¾&qòÝß%Nà&¼ ÿ/}öÌÛîºú–7m‚Uº¤É+ìÝüæAÄÕÇZl1Õà7LàÇ«Ó„†' ÿfø•áCƒ4`'¡€¡È@Š U†zg 36¢›3~êB0/ÉF¥¿HÂ`ɶèfRÏaÎo3Ö˜¢B%Õ{ ÃLé[3µ‡Gç…Û%â Úíå ¹ןº'þ,• ÿ­'1#áx “ ôþoîÈyðÛ‹²Q16(–3±ðÀÈi}d^³½æø@ø@â{¬ëœ7Z¹0Ê7ŽCµèãj~‰ {´k}dKd—éï#¦Ç½û÷dîìóxñ ÿ™LÏzÇÍŽ›["ü.àã.j+Þ¹¨Bó9\!zUa}!)”¡UŽóDˆ;/§]¹äÓw~ýÙÊ–«7%†?xý¦Ÿ¬\TßвhÖìÿú¦…]ÝMËÛ8Oáƒ-¼ÿþ#Ëvç}î‡o&Ú¯9²þ5<{Þå‹æÕ/j¾¨ûúk×-¿ö6j+Uwœ©ÕxXoœh«³µ®–·É Ë{Ì{Ð wÀ<`{Úñ ôKÛÃs,04™Ùæ8Z¢OXï¾×ó±õ§°Â“‹3V‹SO.LÁÔ`aR"[Ùâ,Vp½ò‰r"µ8{“‹ó¼ý!-¹>MÞ#õvl÷ç$שñ¼õyò¬]õOÖç(SŠ¿,-Rƒ¥Ih(+Œ-]Œ®ÍتΛºðjÛÊÝ?û+¿úg&Þÿú©wÉå×ΙµÖgž›9·¡÷̱áýO°-±7Ñ“Xxà.}ëÝ?¼õ¶›6߀­ô÷|˜yˆ…z7‹âx^Uöq„ˆa¬ ÅöÉ¿z’ùhÔ¬<‚Rü+‘d§$ÉœLˆÄ)@/ ÇÓótÆ|‰ø;ºE÷é†C‹ë4l6^Ã$ת¬¤UØaÊܹ1¥„-Þ!zžÃ–oÏÙå †(%˜ä©T‰­ºçU [ +éäBI9¢&ÂQ]©µ¤Œ =£P©aæÝ0ÇOaP›Êä͆26±‹ü…1y.DçæJ8ãk¹›@íôÊ}ò1N|…{KþHæ4®HŽqåzùn·ÜËí“ãÜ ²!i¦•–ň^ÊÌ´£º©¨$F4IÎ2¨Ù¥+Á™ƒ®ÍÔ ‘L$ÉK84†D¤‰¤TšEtéd¤8Iš4“L•î“ž”Þ$’ÏÉgÒ7Ä!¹Ò iƒ´UzŠˆTOtEG?hTš“½ƒ°Ýƒ5Òˆ‰†÷ƒpï|[Ë=w¦†î/wÖ= Ü· 4´^Ï…Aç —»XÀË…÷b·å˜Ìf”f¥Zׂd÷?ì(î@FqFKFgÆæ !Ãj9_§_¸©œÝSRê÷ܾ…M3eš„B>»fÊ2¹ ÿ›çl|bñ®Y+ßxñ¡}ë¦\>­¬W8ä~¼oË@»Í5üÿR¢¥pquà +}˜ZtÏÁ|\(ˆNë×WX¦[J+ +ôd¸7tÐ|DQEYT=²[g®5×Z$٪؜f§Åigg¹ØÒc¾ÊúŽjØ lð­ËتlõÝœ!*n§b´˜çš{Ì7šï4?lÌšÉè4™Œ£Ëäqç8¬NÜâìu§iAJ. œ ÉfjLGÉj"¦wÓ"½b\‹¼¸¥3„µPqˆ„‚®ó©–5vÉ9ªÑ¸9e 2¥pnãbÒ’ßl¾Öú +¶¥¬?°FÖ4S‚–¸Sþ¦Çä +I(d³£*yÿñÞæ—^l¹veâ§ïwÍ»|YåoÞ[YY?-ûéÏ„Cõ¿¼þÑÒÇßüdâ÷¸êɦàðܬìÆÉ3.3 +t7œþä_Àšƒë Ú2æ¾:†“Í&›ËmÚr»Å ¦îÜMê|óü¬¦Ð +ã2ûò`{îò1ë3nθ;h´‡èN•ˆÑToóùc³³f‡^Ìz1įÉZº.ëºÐï²~£j¾);+;TaŠ…êÔ:SMÖ”ÐJS[è*ÓÕYÛLÛ³ö¨{Me9U1‰YbȧúLî,)+¤šxìYàÕ}Z¬Ã‹;¼»½Ä{ˆ´¡4X}FE §894ÝM÷k1z§Ñ€[ðNÜ‹ãxËøO¼î¯°ò˜/ÈW¼_x°Gwxbž:)ö"½Ö8XPuøk[’¾‚·S2_7·q?ÒÇ71[\YH£]Ô€\=Ù=–L»¢Ç@Ë'—,³ð³€i“€‡SéúY@H ôFŸ–ë{…I³W¨,XhÝçºÙu¦ +ÕKƒ£"zþgô`Ú5A`*Ë*:N7Mɪ íQÏRÙ9_ÒÄ;{Œae±qãJ5>iÐJàN{Ü<“,jíÎÀš÷–·_tIlðO-[6}ý8vb”8â¸öÚë¦ãoõÜ:‚^H|™xœ~ûÖ«fǦ§Ù '.¸êç//ûË/Mk–”eUÄrŠ–]ùü-{ÆT¾Æ€Ndþp—*RŠùb¡AéOs§"‰X 9ƒ tU”À7E›è*‚¢37N²™ì$<ñÉÃO¥¸2»q?®T&ÏD!¿ôXJ'U2«‹tR;’˜Éÿ(1‹éôéï&Ñû*ða²aT>´]/É’"YA‰(Ë+ÒBeõnë.Û=®ûÝ{­Ï¸?p}*ž &£Œq)Ç¡ šé-jLÀ–˜¥§5¤µ¤qi›Óˆ–VœÖ›6”Ƨa° 5_±oÈÇù¨"ðŸ·2›<¹ûUÒ3…ÊÔ]ëGÐ,q³¥ ºÞj&¡,jŒ—ýç;®Ù¸Ùs‹¯;òó·?ÜèÌåÿÇçÇ_zåò»ÎEÏ$§?º»©õþùOQªKI·PÛèö(5C©G"6èþ 1Œ¯~H¹óÒ>_ìÞŸé +=;ðAd-!ZèªnrgÄx " Ì=ÑèG.%å(ÒêgÆ¿)ߨ3 +¯ o¨¯?Bï‚uù¾ñKô©¢<É?,<©>j|–ïžU_ç•B>K(R5ãýü…ûÕ»Œrê”@Æf“Hw$s0i (ã0H‡ü@Òn|@wQ+r)-Da‰g^ãüy–"SªiO¿dàm`¤¸_Cq`¤Dÿ‡ŒâÑ0r‚ª¢ ”T§Á *¢nŠâ”e…7)“:áŒàšðFNP ’"‹²$ ¦N—°5€üí8€‹uUŸ7<¯Q[ŠF´ì3ž¥ø}3‡›ýÞáa¿o¸Ù;zœ’´­©?6zøg«HðSræùä…IÒbäš”õ@£5Íôð¬G¤ã¶ÄC¸ècl½ˆ‡ó$^Wöc%÷õp`ÀšœöÝ,¼é#Ÿó…ü$B%x¾BòËéB†Û?#mZúôœßX?±)ã|µ¾…áe¾åá›Ãwø~ìßãL{ÍÿzšQM.·èsGÄÀKF“:†ÊîËìòÆšK¦–Z ä÷…÷ÝÙ´í™ÄÀ­7]’^î“kkû¶^ú£ºô-}öÔ‰‰·í%ÞHåÄ%áòì6 ù†;™e™Ž~2ˆì#§õ±†Šò´‹Óˆ}¸@]à^àmJÿ›$–ñMeiSù:ScjÚÒ½Šj4ƒø#?0¡Oœ”ƒÁ‚TOPöwfâLká–œ§q'¢ç³¾Œª$½×TÎ<>\ùÇY`q&íÍãTc·¦7OiÔ ËÄeê2÷2o{ºÐ v2ó±éýNòà8âr€J={¿û®ï{)‘¼l¿nM¿ªù†—·Ý,>qgâ³Ä7‰‰.kz€ä?Zß¹ûɃþ„êÒù0÷*X >ô;}v£¥ÉÞä^ai··»¯õ^åÛEv_µ¾êýÀú¾÷ ñ ù ǮӢc¼c¼k†}†»ÖÛdl7Jìåîr/·^XoÙ"ÜlÙæ{̾×=h?èVÌLBÓb4=`wÆÌ¥&ZãËŒ±Ôb‹™a©@3»Í€tE:À¡Ò §‡@}ñðHóH˜Öâ *2ÑŒ)X”?M +:}þÆês÷dÍ3GOÒïæcÑäÙ¤I‹aÍè Yò¤½\ųwdüØĘ—Ô·_»éŠ†e.쌞üÕ‰ÿÀîã/}J¾*™;ïö'žಎ¢{ ‡1^jÎ^ê‘ÌÚµ¦äf§^`o›Ô&{RZîÑ8­(™›3É.fœàŠùfp5Ʈ߽Šâdâb R£› ’ÙB¿¸éÉ3›Â˜JŠÅ‚ü;¨ìe_FcåÙ®9•”¶$Oì™ ²bjÛÕv{RZÄæ¦`°,5A{i‰|«óE…oM|W½ÿÒgß%^ê»û†íE5W·n½qùÒ-\Ö„#`Oš±ïNb=ÓùÄ%«}ä™w³Û™ÏùÈŠ¥ã‡‘ÖI­¡â^å>ÓÝÖÇ„½ê³Ê³¦¿,;ñ4r±X«Ög>f:(ô¿¦¾n|_=b<-ýÍdJ·¤»tÐ.Ýl‹Y\/¸Þrq.& ™U,5{ %?ÒÁ‰±7˜[ÌÄìµS»÷ /-†Kíìø4CK£få%ÓhA2õ¦³T·€:í¥ßz¤_(]d·™ûyƒÝKÉmP¹’BT”¹(³#sw&Ÿi ʺÉ‚§´aô‚óÔã`öêN¯žë¬òꙈ@{©®fVkÕ03‹í0€°ÓÁ=¥ªiÚ7 +z2µ‰1ìtÐ}šÄûu+V«Ø6×tŒjÐfÖ½Y*™i§fÚ½Yb% +Ø%ç°µ–2{ ´¦"®‰EeqAf}9’ö±‡|‹½ã¾Ø—ø›Ú±óãØ.ëÜõ­“/pü ²ã9E÷=xàöA¢‰×Ï_{Ë4¼êêMS¦¬¥zà ààY¹Ñ€^2ŽÇù¼fÕlMüf¯ ó/x‰Ëm#N»ÛfvXÕìÀÈJœŠl1àE†1PF¨"¶YÜxÄÝ´˜i…vO@Ӣé*¥Ur½Ü sr®µÈ¶ÈFl˜×MfG˜8¡^÷›Ð·ç*ƘÛçÙ0HÚ“ïEA¥Ò÷Ï4ƒÉœ|õ­Š:¡ª ª(±À'µ9J™ÏP⑘VpÑ7‚¶÷Š{{6¬ O™tQÙÛo'>{€7Ü|ãÜìW¬³ë>>ó 7­ýÄl¾…YEx–¾x}Æ– b7š:ÇÞlÚ<–×0xË\1.%¥œŽ§)Üe–&gS΂¼Àª+,§m§ö‰¦R÷ÄÜÒ1à&ºërkÆœ0{ÔÛ`Ï6M†|£)bv{\RÞlº°ÀÝlcBÒo0&ÓÜüäå$Ó±±äBP\ilã_$P…°DhbV (Á .Éëóó a¿—*ÅçóûwŒÅcA è**ÍÚ}ÅgµÏÉ”þ±·ݬ†O¦NtF÷ÄÇ:ïæ0ñe¯R¯Ÿ¾S!ÉÖÑ-n Ó[–vg{Îò¼eÑö"‘îrÁíÝ÷ËDvMØSx  +ç_H_…«åŒÜ«Ës¦Cï_»ã~±K“:ŸÝ‘øËïÏÜвü¶­+Ún¨ŒweÝcC—ßÿÔïaöÿì®3?wheåàmfrÃã?yð§öþˆuxuM ×ݨOZpWPFZ'ãɶÇß`EÜB6i´­° ‡ÓfwpN‚-”¨œ¤¨ªÓ¥º2¨aYѵìØ>(Xñ³7öÜYÙ±Þ^/éôžð’¯½Ø‹œa·‹©-€íuá.ìòyª’„;u-¹S©RÒ‹ú8ÐÔÃÌ+¹2õ52‰ D9ƶ;‘fñ“[Ÿo} >#ñ™6û¢ÚÕ¥‰ÏÀ,øt÷´Î­;†o'c÷^ZV³íæá¯`Ò Ûì}Gv‡ ¡õƒH¡·6µJW²Y‰+CÊaåkE(-Ê&¥*N”Às°‹é쮀CÍ`‰‚(ñ*‘`Ïd²ÌŽñ>95¯só¨bË“]oXSFbWtô"úÇÉ‹hþ æg¾›Á‡¿û8´ 8´Fh@ÿIOG?î7ÙØÙ¯~­¯ &qVÎ!F”eâ>õõuåMõ#U˵pÄ$y•Zq¡¼N*ŸðÇù3ü_Ea–4K^&^ËßÊßÏ? Ü'Þ'Ý'«Þ.Fù¨/æKùr‘©Ž¯T°IU‘UAU8‘7¼H_Ç6dIåTÕÀ+u¿P$W$,µ™ˆ!Œ7#@ôM–ª¦Ll:oŸõÔ/¬(ê ˜%ÏÑåk­¯È•çŽ‡_ïS‚©«eêý ®æäM ux‚X²mÃ><_š¸ ß”øuâ¯7€³s +¯K\3|9þx[â©Ñ·W7ç²!=òRhÈf!. ‡…¯“×@›„^¨ýilVŒF¹†|ü?p-ŧÔË®©[Ÿ‰÷€VŒà‰ƒ(°›¡/Ø…Œ.ÑmŒq19æ…jÈTyª·&dÔ¸¢¼¹JKÞæ¼Ýyˆ{¥=Æâc<ïpÞÑ<3Ê+Êk€/ä}’'æÑ—¡« ¼™=¤ /ù3è¶Ñ§JA¶{ð’Õf‹¤¥§‡#*ˆžÅ¶ÛôKËZl¸i€ÔêZ8#ê:ÒqK:N‡º§sÂᵸúŠ0#D©¢©>ÆЈ^ ¡Bv$Ñ'\+Š¼ù$ÂY"Èæ‡"Z¤82á#¾Ü?TŽ:Q©£¬¤®¬<û=lI§Ö4G+Ï-]æƒ +=ï>¼+J·%u]Ô?ò0/ÉãfK9rv)Ÿ[Õ1wËв»‹kúAÏC¹°¶3"³'®(L|–Y5®zEAâ3>|ûãóæÏŸ·è5÷ 7‘E?-¬œvËÝ Bjï¿tLí÷ŸIÞÔñMÀ37Ú­{%‡Çq©¼Bæx ܲÖÈ5–/¬‚ÈT›M2›D£Á¦*Áa7bª áú®ê?Qmª!l4SúšLƳΈOÀ.w¡†c”ú%—\£Vnð•ÆˆŠŽoJ|–=»bzw…pË;Í÷ÕHæSmãnìKøðOOYqã©^›öë}0Sx;»ôiŸãÏä¿9þæâ_#Ÿ Äî| +i².p,p7yw‘{Ä{ä]Æå=òá·Ê{ÆÏ„ÏÄÏMÖ½ò›ä¿‰/˯…y›x£ÌÙ˜<”DN^rVHþ–´Î4’f¢ Ü“¤“—4ÚGw?¥Ýº löv/éÖ‡›1;L %ß çœ·ÏÍÙ>üÀŸq,ñÆWw$þ¶kw¯^}×]«WßM²nÅâöÄk_ÿ9ñò#ýô±Çzxì1:ß[«ø]0_+ø'÷é…ãÓÄã*LŽXZ 7Ý4ÝQ“öMšB}ÜQ¿å”ôMš ëç|Öm0X-æQÖ–g6[ÂV+sT ïÑÎ<^ Œ´ûŸ–íMt¿§>íy~ +}çÃE%=õ6T„º*çf} K¾r“Ä™ÁÆõÀb÷mË_ó’å[µ KÿžNœJ|X;ø n°ÿÉŸôï}ˆú*—ÁÜÃÜm(ýD/·W’˜)æ¬LŸAjL5ÎérggÈ.O¬IhRš8š×]ùOg=2mÆ;<·zïÍFôÝz0sëéXÀ?§§T6äìÈ!9º7#–ãîêaßiƒ‹Çà¢1xLf°„«QjoJ¾C¤V%wfzKì‹n $?û ;»KéPön݉Ž£Ô…J™ˆ±ˆÝ8œ5.Xœ‡›ô]ªæ5ô­ª®d1IŒÔ­Uê%Oø4'¿‡‘=ò†®ìU–\ˆ€_4UÆ +ší3Ò{°/÷*˜óŽé%øèû¡ô{‘p$;’z÷‚ -úýzTZŒýöÕK®,Ïqº¦'žºlãGŸ~ônnâo¶EÅZz¿ØÔxòë‡qQtÎüÜô"Íå´ÕMZpïöçn»eì¤Éw(Ó•¾lFÝÍw¼‡UùœÜ.üvÅ_éyçUͳL0Ï07Y$Ÿ y9· yì'ö؉{9ER%£—’Û‚<½ž¸‡kdÈÃyÀIïsaºiô#ý^b·n6”"µŸ¼´uãs½\ØcŸïªrîvîsr-ÎÍÎÎÃÎN9­NÍYìä>ÿ†ÞQsª.^zb"{§Ò92D/ÅÎ$ïĬ'™œ}Ÿ@ÑïZ”¦|üf ½“ÑÔ#¦.›l¡²Ò²¹zÈIÌð.¾æ’«+ Êu×a?>š˜w}4=í£üÒÙSÇÞ…ß:úÎ#‰m@Ÿ–™Ë‡ÁBz@÷,´-·Ý-pŠè+I¥­ŽÔÙ>#óýl¼ÁT—Ó©*¢Ãv¹Uf7³“’ÿÂNRä³’ŒOÈXþç.`r“ù;û¨9yàÓë5ç¹›6nÖ„çÛ¯xâì Ì©šÖ•}»ç/¾ü‰»IoÂ{´mb}Ï1<NÌÓ–à¥0ONÓ]B®¿(&ÑH¤‘L#p±ŽôCÊÜ9Í?!vEΠ˪Ñ>+±s~ůf¡Ãk#¬íº;C‹©H08‘σò 14Á°)©1›Œ¬-ƒâ‰ñ)XD*ª¢oDU¤n¸t»©¼AUB°y¥‚žëÞôܘÁ0›toòxüVµJ­7l€ëžTø*¾žçøC¤LÔͺÅX†°*„Ã>ã+ [>*\QïÌãÍ°S5ûØE+'¿cö + C`K;J¿Y‚“oíâ ÃC/*à‚=“˜‡#¯Oðˆfë/q0Ôþý©î‚’™¤©Ñx ©‘Dô±@Y‰D•% ¹I&oü’SÉTmF#»È *¸ +q7M¼‡»Gdççú†1 jß iÍ Pû\2é{Z::º °]„ÚET¾ôrnJıíµg°eÿ›Ø•x2ñ—gž›Fhøî#òäð|2#¬Ü¶rïÑoÉ•^çÉ=Ò þ-~O:adÉÏ{Å\±—§á&| î‘Ô0ŽJãð©Ïî1œOKJ–òÕ?AÂÏR_æåKÔy|“º”¿RÝ€¯Uïäï–©ïñ¿UϨ&Ž—$Eu󟯖òUj-¯¸xŸ:A¥^¡îåŸáßPOñŠ³í·{©¾8Ò¶6OÍ—Ñü*ñ”‰ÈH‘é |GæÄFØ+¢Gu‹;;Æ…‰â$DDƒ!õø„Ó¬îdž0œ ¢ €­*+Š äÊ>±T¡ç0¹­Þ´ÛtÔÄ™8ZMJ ´Ú~"y-üònÛ9M°ÆK8}3­Í§X%µ+DôåÆèšÑûèdnôLÓS‘ôª2M'˜<°æ5kº0J1ã+¦\5âM‰ÛñÂç^Å3÷àm‰½G>"!Â%~‹³Êð¯ñôÄ3Tw˜³ù9ÀUŽ=mÏ°ƒNÝk´Äd·É“h$ÒHpC¡‹+:YEÞd0‹V‚"ï £ŽÆŒ— ;=#Î6 ³BÙ9áH.ÊËŽ)(,*[R+Wþ?îûÏgÿ¼ÍÕ&î)´ýÙ§¡‡tî©~ÉT¢@jw²´Ï-‚Ì„RV_pgÉæç¸'Ñ"T +ÕOöͧÕOöë5%,-˜L‹Æ²´ON>–œ%j? A È’ÊÕCØa7„ ˆ0 'Ñ'F pÜcÜC}µháQhÈRíäé¿a£æò(ú:UÃèîWŒ´û‡V÷0`Y ¶BØ a„· ¨âÝFØOîîæ‚g÷â쳬Õ*÷S´ áîCL ÿ!îž~+£Í½ýG‰^måîB Šs3ÑÍÞh·#àu}c ëúUs‰àoAß¡¿†Û 1fe…¿\3Úü }Ãûa_q,™é·zK€ +æÚ¸Õ(„ÜFHA¹%f@º˜[ + ŒŽSï·XK6CU^Ź`M¸jÎJ ­á芣`=}æd?=}¹ù%0ã)œ—X8ŠA*sR_I@{–Óñ·ö+:¾­}VWÉóÜMœË8Àm(OÀò<§gU6“yýŠ©dgµ‘›Óœd À1Py5khu4Tmã¦ré°¢Ü\¨ƒWËe²t/÷ ,÷÷“þpz`èYîÇ ëÚ(t?))Z“úMæ’¡j…£¯ñŹۀ·±Îwö‡Ç— ê0—‹Š! ñ&ÈmbB¿rÛkÛSÛSÛaPÛAú· žl˜"îjÔÉ­G;!ì†<+Wte²sK9çÂXŸRb¨õ÷+f:2oŸÝÁÀ¼ýFsIÕóÜZóµÐ¦Îu÷{¼%Ïrùl*cú½i¡³ÄõyΓd º)KžçÒ”0\fŸ+¯@™ +raòKr˜‰¼CÞ£ì&oA™¦o¦Ò_¥ÒÿžLG†Èáä¢ oÓôhu:ù”Þr’ÑnÈò,yn€|Dè(ȇdUAzÊK!„´ÒC}Á×d ûý}&7,y¹/Z”ÊrROZ*cw—Tç—È‹ ãäH³!}‘ ¡,H_ ôÛÅ2DºÑë eh"¤O§ÒWÈsTÄÉ3ä ìÒßg¦Cˆ÷I4Ù×'Òäç}(Yj( +=ÁrUä&T@!l‚°Âu`}í$WCø!„k \Ëjº!ô@ ¿7Þ €Ñ  £0:£0:F'ë½ÅhŒÀhŒ†Ñ-€Ñ- ƒŽ·0ZF`4F`40ŒÀhŒÀh` €Ñ C 0tÀІ:`耡3 0tÀÐF1`F1`3ŒbÀ(ŒbÀ(fÅ€Q Å C 04ÀІ`h€¡1 04ÀІ0¬€a +ð†0¬€aeVÆŸã(`Œ£€q”aŒ£€q0Ž2Œ£€q0Ž’õû¹ÃÕ¿”ÀrP3”ÀrPÊa†rPÊáÔÔ»1ˆÍF› l†@q‡wp‡wˆá1ñê@q「8`ÄF0‌8ÈF0â £0z£0zF/`ôF/`ô2Œ^&¸=(Æ](ÿˬ!×áFöZ²ç±túŠ¥Ñ–^‹ö³ô´‡¥?D׳ôjTÎÒõ(ÌRh¥Ý( ã¾@¹¥Ú * Â"vCØá˽á#¤LÏâ-R½´[Ú'½ û¤£±ˆõânqŸø‚(ìŠD«N#&¦GAµ ,Þñ×`¸ŠåªH úž-ƒ¿‰é¶ãÚ×ùø­|üB>Þ—wäãj…\Œy¦é4TN`à¸Q7†'Ž@(G&fºíàWž@_x\`?—Lòô(¤_AØa„ë!”C(P!B€Õå|£ž•jò9Aí¹Á°Ev›¬ÞÓÿ ¢ßï‹äÞ³}‘bHú"õ<ÓY¨VðA¡V>œ{Ò}}cðøgÉ䩾À³<ÖˆAÒÜ)„䲾ȯÕ&<xÌþ†d:æMÓ9}6»/I´/¦ÐùÐQ<ÍÃè¤9)¬ìdO¡¾ÀDH²úZFÊx°ø Øð4åúa@_âFë†ÀñÀ_úaA<>ÔxHÞÊ¡?1¤ž+ø)WúªU +ûÃþT§éÀžœmû¡-œs0po 0p[Á€ Õ?‚qoc]ô®×È“º#°9Pè.8X˜h Ì 4ç@}_àçè0Qn$O 4@ƒÓa9}‹sØkWô@$P¡=Gé‹Æ'Û-/xŽR•${ôÍÏ 2>¿|Ûô|é„´SºLš,M”BR–”)eHNÙ.[e³l”UY–E™—‰Œd'õ£ÔñqŠôu2$ò4æYbˆ©c‡éïèÉÜ®¸ƒ«#us'ãºøÐT·X‹ŸšÀêìKãBh2ŽÛëPݼÉññѺidN¼Ðâ\¸eñ’4mm‹÷„ÚjâC5Úþ±/Ïã—é㱡šýèå©ó÷¿¬·ÕôÕÇN µÖ4õWU6V_Ð׶³}5V~Oc•´±FÚWUõ÷<®¦«h_Õ´¯jÚW•^ÅúšÚN御q¿Œ&Ó£–öƒ +2Ü’lšì¶vN¢=81èݘvˆGø1dˆ6Å¡Éqú¨ º š>‚uF™¡Ú’zäÝ81˜v?–zd…j[hòÙÃNDèõo]<8÷ÒF**q½õûy¶–~Øc/šÚ^ÿ ÜÍü‰Ö~ï§ûû>===kiÔ]‹P]<n]|½Œ–$誥¦ ê +Gë8ŽÕíW”©#Cð0 +ƒÀÝ´;š‹bú:¯®‚×%‘^±W"ÔUèî÷g”t<;ø&àÇ‘õ}EÌ}&ëû³r¨ÿÒÝ_T–LÁ]¥iŸ?XBÏãÊ•¦9ÉT·@fgÎ΂å½9½½åôTýਠì¡[i_ÑuG׎²ÝM(ù–1ô÷`_z븗f¢Ñ¦èZvL†þžÔgO´£ç»6ÕêZÖ|÷(C’õkS'’½÷Œ¢õ¤ØÆ”l$Y:û@)©žÁ3ú ªD;ý‡¬¨ÍGHü–¼„¦ÀÇÁŽøR|ÄGÈ¥ä~n5ïç÷ +ÕÂnqµd“&IßÉû”K”U‹:×p­ñã1ÓÝæW-6ËuÖ‡lÛ#ö_8.rNq¾ìÚïë¾ÞãõÌòæzâsù~ìwúoNÏKÌ(Èø  þª=“4)1 M±¢o¿ýöjë¹ÃµÑÏx½zž^5é¢Ê‰*Æ——ÅJKÆŒ‰æçåFÂ9Ù¡¬ ÈÌHOóû¼ô×a·Y-f“Ñ *2ýÖ>ö˜©¡Ú-n‰óáдi´j…ŠÖó*ZâTÕ^×Z˜v!¤ËþROBêg!±U«D•c´©!-þ«š6€/ š:þ£šP“?Îò3Y~'Ë›  ‚6Õ»¢F‹ãmj¼vÝŠíS[j ¹ýuJhJ›Z0íW 5@.î uîÇžI˜eˆgê„ýÉ&Tܪ™÷…jèâ\ÎÔÖ¥ñ†ÙSkÒ‚Á¦‚1q¯1ŒW¥…šZkÒ÷;Ñö9WõûtÍwá“‚1û­¶$a÷›-©ŒÑt~¦íì3–cà4W7ç,e1Qh:D\[¢ÁHC0§ñ4j¶/`ði€_ +i+SZ¶['ÐzŠr¬!mû_Á†l ÿêšÖT˜cý+¢Y*'gE žæãÑhû­üht'ï7;;ûÎ<óÞfv÷dwô·'…‘±Ó–^Ô¨ #šF= ‰™‘FùîüæÖoÝú¿òT„b¾ÃTáj®hö¶:”‘î)w—{ÜU¾ ]lX¯IV‡ +‡ëßU—Ég&%f†óÃî·c“%…w© jCxÚÏò¿Qh…Û½¨U$s1—ù¤;U!}[’,|x[YÈ¥Á.FÛÄ…ÃGnW¢2òÓssï‡Ýø(m@¼IF<à>û†ü‰î³ .ÈÕeê„;T·mÇüUÚI"vÈ-4ÇM‰“Èâ +æ/ïZ¬@ ÇJiŒûÐŒ3O–ž“‚ŲÚÍr—Ð / Ôí”bWÆúçñ.nÁ'¼šêŠÁ<ÿŒÇ%}¹îeÄ` †I3àÓubX†åØ/O¹›Á·ÅPÌöz¢/úºƒî6Úc±YjO×ûJ°G<7ÎMäUt„UŠ;å>E2rñ:¶SŠTšhIx«$N¿Ë½x¾Äª1:Ã`OYÈÁ<0ÊpDËP{Úָ߸‹œY¿vÄ4—¤³ RëM¬ëíÎ »ð^ðúb¦J“Ç95ÏÀ½âÞASì”ú²WÚŽö¹Ú¹n{ ±ÄÓÌ~Æbâ0þŽ¯T‘+Âd³çC’ ‰’L‰ŸRqjŽš£O øTÕ¢‰W¡FvcöQ6C5.Hi%ÊX)‘¯T¬*TÇõj]®O1›(ï$´¥Œf`=ÞÆQÃq±Ñ—½ •Çdª¼(¯HµŠ¨+ꆉ1óÌצÖ&ûÕþ×n°»Žh‰‡0E”íëØr|€¹Ò¸†J#é&dD¤Z®¨zª¢¦©•j½Úªë}Ðt6éf’9fÎØßÙE¡‚ûM™¿Õ¯r;]m§!ÛOF&%:—V±p‚­„q.°¶ßƒ+–GÙËtY(Ëe«’*¹ÌQ"šÚ¨ª{ª~E9«ej9{?Ü WgÔÇêKu][ÝFwÑëu:¢+ôŸõ禑I6?2Ì3Ê8j¦£ío³íF»Ù¾ck¼ž^¡7Íû"Tšs´¶}í'>ü ~ÄßAÛ¡%ͦ$Ö¢”v_N¡D? âjüƒZh)­å‡Ä&™2PÉ-ã¥XpÕµJVK©¼Åp *Dì)ª¯ÊVj¼š¯¨%ªœi·:¬N©Óê*‘7×I:EwÐYz”ÎÓS8†zŽžOÉ–è2}\ŸÐõú*µÖÜÜkfšÙæ%³Á”›*ûý%S©=`+m•½mo{ÊkéÅ{?öó6zçB^¨KhhèÙÐÉе˜i/í‰<ñ»K3¼yü^U¦š˜"¹Ê bpGžB=dÓ+®áíS/ ƒzbkªâ‚÷!sÍÙÇD‚ûÚ²åŠ<¥9˜jl—³ªÚüQõ‡œÕâÌ=ÅQ­±™Ñh©Ú«öH:ÊUO•£ÖhÈ^9] ½?‰å2I¦c³\•îò´t•"œTÍt¶ÌGOWªŒÔ“,©`®)Ä£ÿû駤á,.ùkÍ÷ÌSŒOXInÁ§² ·Äº+ŒnšÑ¨€Qf1íýQo ý¬ˆþÇ2Ù;ŽòàIr¨«×ÛÌF þ…Kv7-*‘ô¢?Ѭ5ç]Ww?=Œ^†Ñ¯Áô§Ç\ •ìc9(¦§×g, >–1£Pˆ§õJ\Ä­qóܯÝT¼OÞ[’*·ä5zD9zâ=¦çñ‘,¢öÿ?ýró Q‰ËÒBÚJGúÃU;Ë.µe¶Üî·Ç¼”ö|¬¦EŸ£5×çÆ¡ +—qCb¨›8¤â§ÄÛØGb²ÊÕû!-1>ÛŽq<½n$ÓÙJ1¥·†þ¼¾QÃ81ûqZ”4çˆÆ±ÿ¶3rþ9Ï~“œ';x¤Q»=¾ä¸J75ƒýõaK+µ*‰é,>§´]W*ãB?Éa[70…ì¡ †Ê6jàm¤1²öÓG)ïû¤Ò¥¼A¾|zhC$ Íž…T°ë¦&ê}œc¿ÆÙ«zÉãDqÇQ‹¦2ýaÄpB´‰È_¢(^RãÝý„?ïcuÒÇÌ +õ£Xµ>©öòJ+ƾl;ÑÊZÝÉu~¡ÇXÕ dT°Ñ Ú»J<™àû£Ü†ÊH“»mOøK§PoÙÞ✣Ï$ÿ\ÆZãwáÒèUe˜DÊÐ ˆgy&÷§3_f¦#‡TJêDDJ&¨£lR_ž{Øþ ­$–›óx‘ü‹½2”°¼Êæ`E´>².•ûËBK"Ë ˜?Â|x]{-¢ûçñB†gƒ<”€9/Type/Font/Subtype/TrueType/ToUnicode 8 0 R/Widths 12 0 R/FirstChar 32/FontDescriptor 13 0 R>> +endobj +12 0 obj +[277 277 354 750 750 750 750 190 333 333 389 750 277 333 277 277 556 556 556 556 556 556 556 556 556 556 277 277 750 750 750 750 1015 666 666 722 722 666 610 750 750 277 750 750 556 833 722 750 666 750 750 666 610 750 666 943 750 750 750 750 750 750 750 750 750 556 556 500 556 556 277 556 556 222 222 500 222 833 556 556 556 556 333 500 277 556 500 722 500 500 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 350 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 222 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750 750] +endobj +13 0 obj +</ItalicAngle 0.0>> +endobj +14 0 obj +<>stream +xœì½ ˜ÅÙ0ZÕûzºûìÛœéÙ—30Ã, ƒ£ÓÈ&"‹²È@`ØfQ@\PÑÄ5‰â#apIB4.|’¸$šÉ4j‚!†#Ì™ûVõ9ã`Ìw¿ïþϽÏýŸç?CwWWW×öîo½Õ ŒÒQâÑöÅÍóüêç'AÎoáºbÞòæÞxì:„°!óÓEËÖ, ¬}§!/‡Ðœ½m­]|¯x'Bí=PþéùËçµõ~{¡Ž¹ðŽ·­£¹­àí / ÔÙ…ú£åóV·ñ3ù÷Z_ åíe­óç}÷ƒÜO…û[C¼U²÷¤ýü…m‹–ÿüâÊÚ·ü§óWuÙÆõ'„n>0uÒ”òJ³²©¡­£ ÀÜÅË»V›fäu¸Ú×ÏÜÕpó çõ—b"¿ÇþPTJ®?åÚ¶}±«w‘‰$hÉ´¼ûÆ< g–¼pvŸðÈ€;aN¦ùƒÒeZ‚Æ÷¿]ƒö§ËüOÒù +à©éò)¸%;ÓåIþêtÂ7¦Ëˆ¨‰©O—|æÁtyØ)é2 Ùy´ i]LÓ<¤M¶‹¦š¿‰¦Eš¿¦%šÞž®ŸÔ¹›¦ZþG4­Ò2¯Ð´FóߥiMHÓ&}÷3šöAš IûIÎKÓRçÎaæ¦éy—NÓ1Zæ2šÎ¢efÐt6M/ é|Z¾¦KiúšDÓ[IZ¢ýç¢i·­'IZsó÷Ò4 ÷£ô|êÌ3Ü+éùÔñ4±+='/½œ.#¡ +µ3]FBaõñt’8]†EßPÿ”.ârm\º äkøÊè6íítÖç¦Ë@¾þ½t)žòtñžºt’ šŽšQêD-¨­@6àÝ` ã!p®D«§7wt¶´®°kW \¹ú¿.Œv@N%ÜW ¡šŠCiM e[QZƒÚhÎH¸ë€49σ|·¶ÁðdZ6º +òÁû]йk†+iyœ Á;ìÊŠŠ¡öÔÅÍö„Ö­]kÚší‘­m­óº »ƒíË–ÙWµ,ZÜÕi_ÕÜÙܱªyÁàÿïº7bÄÈ£¦&¡xœ¯L€æúïFt´Ì[6àá—)®¤ÆyptÑÖ@ËáÚ–B^+Zøÿ ç¤Ö´F÷½ip×w¤¯6š©yôÎmyä–ÓlZ÷b:2͇»•ð´‹ö–”Lb·tÚó쮎y š—ÏëXj·.ü·p±[VØ]ðlÚŠ–®æö”®y]ÍðòŠå­v+<é°ç·®\ÑÕÑÒÜ9øßi¡±r—‘^Õ¼hå²yh"}y9P+º­Á:[¯~L»›yæ6Lš#Ã[À>ÄîaȾÇAöûì…•_Pë—ir×òµÏ~A¹eP×Àöh‹ÿ¦ÎePfÍÀ{.Á áÆsc¹‹á\wA + ÞWËD8¯¢ t´ïÆ›Z£" ïHOç¼ÿ¢†¯O7RPØôn \WRªtì"ÐB +ø.š× +ç4¿Îåš~0ÛÓA‘£+ àÜûy´¦6 +þåa2(s ­#ƒNóé[™^¸odúÑ1 ,i¯êX otõ#æµ´ßóáüõcpïIÙùÐÚJJ (ŸúêL7–ÑT1”/+!ŸkÒýþúºWü/ŒýËÚКA^%—Læ÷á×` a^د‹ÀˆŒÄKm/CÞ¤~w¬ çZ:òVŠoÿ&Ì»êÍi2ÿ*±“Yí‚r+雤·«ÒPÎÔCJ.ƒÿ nl]i/Ÿ·Æ^ÙÙ üøçÂÖ]vW«½ ¥³m<–h·u´@æ|xÒ ×yv[sÇò–.Â8¯YCÙè²–ùÍ+Hð€ÔÑAsÛ:Z¬œßExíµ‹[æ/Ð\[VÌ_¶rHC;Ó‰ÖËÖØÅ-%vóòk î¥Wü—­Óâ ZV,²;š;eÏ'þË\^Ÿ®ë":¢âh¥«y9-Ðê‚ÖkW,k·àÂI˜ç„A¿Th]ÙÕ¶²Ë^м +†LÊ,n^Övá þ?äÿÈÿòÿŸj*ÿNúk<_-7üëBÅbø¿ÕõÉÜO ã&XÛIµ¯.T’éápW+L«=ÃZvñ„–ù­­ »JþßíÌ×LÉÔþÔ(:)×ö£æ$xga? ÑÖ‰†æ¢áÄ4¶Ò¾t¢2È›L'ÕE势qMÑÅyIœL]ù<þ{>}·÷ÜñÆÛî;üÃTvʾ ýfG+fŠMFVLŒ¼2éò‹áÚa¿áéé;ÝmšÌ4H|Þm4q²[×iâÏŽ¡(Ì4Óía<ÏzÓ}LÂï+ýôå!«º¨þª‚¡`Àdz7ÂŒç^\´vãó3'¼žºŸÀ¿þà}·Íüå¹Þw?M}–’ —;Ÿy zF›ËU¬*1S8EÖ<†i‰‚Š™p0à÷‰ˆc¥WECí>¯eº¦ú9‘•°"ð*B¦íÇþÀ®ï =ø^G翇ËW"‘¶­.’L8C¨ ¾·©¾®¼ÞªƒBz%—!¨ÉW ÁqhmHƒ!±°H‹†Ö:ƒ¹Ì‡ïfý‹n¼~íÅ­«‡Oº|ت®ÊÜÎ;‡•ì5ÿÞê²;K=5·L›tË—O»kp„àÒ3©ßá›Ð1¤ ‰û çïC×&;…”а‚ë‘°pƒ„aâðIh°“õh;ñþª„ø€ôÎœ4O²¤>eöR¤RQ(ç'=Z{àØä«+놲ǎµß^8!2o´;÷0K˜åÀAÊœHÓÆ2ðh21Q¾ +D¸¶;Ȥœl2?DåNÁØÛˆ59L îÙ¿Ÿôþœ¶@ïYTà„ÒÙz·‹»·žoçh/Ï6Qjp;uè¡v`a}dê²,šr±}¿Ûë¯czú~çØþºûYÌ°°»X†]EÙ@WPNa?BÌG€—; qnßZ¨¹Þ¸cݺ;nXwyóæðÅÏÎÙ—ê{7•JýtçÏáï¦îÿËi¼/ù´e3Á•÷€çv +ÚåØ,áìK¹õÌ]̃÷,‡e$ð +óXcð+ +í½BÆ„0ás=}'(÷‚Ä'ŽE§õP€Â,;® L(|¢ï€,á33QÁc›wx†¨‡p=¾¹¤Ñž„yIº?¸!2ÄaDž4¡¦dNž%b Pas®{ÄSïÿÏò.îºKÖeÿ`ì+sÈØê—E[¿”Æ%Ù2õ°Ï'LÓ *YM|êȦ ©„ŸO ‘‰yšˆ{àIB#=Oô0‡QB!;Û´@Xg7(ó9Cå§HOÈùh%A^¦¿AÍëehƒŽlXL¦Žêõ1Ó~’GêÞ URQUfZˆp:‹_×ÁgÒi6æ ½ˆ¿H8Ì¿ _’^Ž‹ã´Fmªg©¶À³Ö»Öw«÷yïÑb§£Ú ês>&fÆÍ,3a +?ê;D@~ ®2@+šPLI^‰GýñxTŠG[HÑ8«'Ìæ‰}“,lõàð~2D§ÃÀŒ¦t†Þ€Ù&¸Ž3AÑ2ñ0G³ö70s˜Vf=Ã1‡˜|”ïÚã";ð•³IÂ^ˆ<¨o8ÕÛtÒʈƒ-žÁI°—Ó¢  CM¸©£±± SX :´¦PŸ2a  `Ç  ‘Ï×2¡‚ÇúËÓ^wãwðAßç¿xãìeOýä±Ù‰;GÔÏ?rÃÑ.ýæwnó½þÎ';g<óü·Ì˜2½ïC.˜’ÄiÀ©‘°Cæ?G˜ jRƒ\’§è†f$¥$ˆs‰’8_¢çéZ8âÝ6 òÛb!")^XN¸Ï±rò‡¼u DNüN½h¾è­3&+ÉAàWÌëA}´¾YçF[W[«bìUÁeæÿ‚àJ}³~›ÿÖØ÷t…·YŠ7ª¦{8C»˜€ÅÆa0t\Ó­i.|ˆyE˜ÅNô’‡nêÞÎ9v«ÍØa‚Éö±³ò¦BŒ +ÍBz|æ9ò¤pÛ p¶7ò>„‡ 9â¨_r«²|Ïž âP$<ëL²Éå[½' r‚œ$ðtÁ ¤ +jÅí Þ Ï¢€kû“ ŠäŒòr §wgß»tý®Ç®¯ºÂïU;{6/iÙêïÎùä«_YºpÁÛR½ýã>|SøÁ-»o\÷¨ÿafõõóoÜ´ÉÞÿÒ¢½ æ|gpâ‡wIýýCètx€ ¥“Sè õÎÐki;´—5þ +ö +ý[ëGšÀŠ¼¢²"Ò€Ø_a9?Ër¬ŽM=ç0sI`lwÄqP½¢p=ÌÂçx^q²²«• 'T\ÁDŸR ¥ôàZGܼjqCN¸Í`:©º¿1&c3,C^&ï@âäò³ßÓƒ·Ò™þ3p?ÊÏöRo~hR>hž©?[oÕÕQ-xËà$$cL÷ÈÙ3"d¾·XΛŽZUÇæªc¹¬¬zRE#Ê8~ÍQë´ “ë4§°NËÃuPå¶`ðÔà*«*g±fîëÝÄ|÷›/¾ØªÁs¾Ç8ù÷RQßÛ»ÈþþIà±Ó]Ê9ˆ0ŒO'Âq’â^Â*Tƒãq݃‘yA5š TFx¡‚G€D½G2a”x)ï5èy|tMÖmY÷ùžòýT{[ûML’}aOi”•+ø +õð1¨Ãô)¯Ï÷ŠÇð{|~¡‰8>ÒdziápºSÏ~ƒp5Ç&ݳ昭æzó.“3H”HÂ…Í0ÎIx›í}× ß H5l¯gÿ×Kö…Äò%¹4h„´É‚ƒXx[¤ÁI ˆ(ã£<·ƒ¶uÙ­ør9,Ð +øEÐ +§ý0ð಻wn½zkñŽ;™wzŸ›´éî#XêºãÌÏ{ñó¶Û>öÐÞI Aæ¯Ï¦VÍNýÅKwï=A´¶ ¹ð¼,TŠ'¥¹^¶³ñÌâXqÂѱ®ƒ¨Šñ¹ ¿®$0*0‰£œ™™‚!ÊóBTƒ ¥Õ­co3–dÓ)óhä ¥Yº|Ë©³ï¼“:{÷5›—.¾ùÖ…‹n>nÛ”Oï¼qýSl¬ä%Ûß}ûÂûKÊŽÞò|ÂøÈ]?ÆSoºiÎü-›Î÷MØ6éÉ 7>ótÆ–%8™®øƒ4¼Õl€³ DPî&&N1hØ¢ µ¨¥c…­²¤Zœ ž›IÖãñ£ÉS5R7ÁªÀDÒä%šÌÊÑdS%e"•tbÚýLÂEßûY¿%1 _ÊN§” +O‹bñ¿iõ¶¾ÒTùÀ†œêáÑ+‚NÞ¬àÕy ÙeÁåÑEyk£×'¶FoO<Ü}>úIðCû¬í»8øppg^²@`ŠˆÜÍd +çØ‚]œ˜ä™C„lœ4‰ß˜ì²änÒ‰ìC¸©À‘­ Åê¶2§» ›¶úqÉr,ÆÚ–|i ¶IPéÔ@Ù™a»¨©75¦%å%LMuá¶pE€L^‹šÌ…˜¢L€âRÛÎàºyS®Ÿ<=¼üÀy,¾xשëÖþõ±gße^ý^×ê½;Ö]ÿ(žb®]qÅú_·iáéK±ôë÷±ùPê©ÏRLíûÁ lõ·ýÎV`¹€3ÁüÙÌRŸñ0Ð#x$ˆ2#Ôsl=8…©½1Äb~TJû–Ú ÿk€ÂÁõdG&;Æ6;vþ©cÇ nêÅ¢u{ÐÍNy§z“úMõqõ´ÊÃ\*µÊeºÒ¬ìWþSUÅ#’6ÅzAà=œú}…x¼òøzŽvc#B¼ ÖsÊ0u8_Î5pŒÍaîQ#Ó¥ú3'A»$®.¢aööž2]¿í$2_&Lu´g:Úï;–v‚ezq…Aï[SWŠoño¡±èjôçj.Ç´ƒ995z•g´g\xTΘü1ãÆNŸêY[â ”àB¹4«°¤&:´ndÁôpcÖ¬œé%ÓÇ5No7,,Y]›Õ‘sxStkÖí9[ +#s²±Sˆ`SŒ¢ +u²Ê¨bð0s‰Æ3‡»Gg•l¢ç Çv²-É$á ¨ˆ9| ü²|CÄbs“c˜“/AùÞíF~…ÙÊÁ!¼؇»†•æCyå1;²]ƒk"3®v™MNõ•®éÔ™^˜2ànå§N5}„Éjh: È™–ŠÄl- ÈHXuá„j«X k‡zkª™ü¼\Ž ø½\•_[%\^n~~”®õ¢œJŽ¸s©žWTˆýiì<ö0Ü­#½²ñé–Ç?ë¸úáºÜ}Û%Y5Ó;nþ~jç±OR׿õþæß±€¯™±¿êóÔ3ý]êÖÔç#§.X‹ŒÏñíó^;ðëÑÓüz*xãÔaëÚ/Û2Ïi_â<>~Öâ_o|7lŸÕôíÞy[XÑÅ“±~×S8÷¿I-úä漢w쾡åÝõÜûÃßœyØ~õ實~÷ûWJ‹"øŠ[¹éÕ…·Ü7bÛüûzåÁú‘/:€=†I͜ϺӉÏ)eˆ\n¤"•ŠGžžËÍ +s‘´XžkÞÂn3_æ_Ž˜§MUâñtf²¹XÝmþMû›þ7ÌiœÎyXU‘yŽÛXDQƒ´$h"Fˆ¬rÔ/e‹š1,Kò$µ9ÍoÉ ž—+ô0mŽŒ$íc‡Á s« .TÇ«Ù¨Yd¯šÌ½Î½Ï±Û€pz0vÔÉÚñ}ݦaÜ›†øºÈ¬7ˆŒøMãí_¹„þ…;¢xX¸¡> +ØROü˧ˆw5 šÿ–Áazu×MÀ¬3õ=º…w¯@}ãw«SÆïN\9sF7g°’x¨ï4YÊ!,°w´7¹Þ²<\…óØÖ—Ã:ËTý‚™ñÞ÷{¿ýè;ø¯ŽÉWñ‡¾ƒŸObfâû^{ÇíD»ôÆRµ|0K¼¨7&ozÞ¼Ny“,´DWòm2p!þ&U( +Êl¸¨4Ì’eŸ7QZZR‚âY ˜·ìDÂBR¸PЈ&€UìT¡%x‰À2ó‚Dj(¬?ÁajA¡'oh +)§¼RZ´,+aS§£ö8ž¥R&ÒÞÆ/º)Ý„àúêslJ^4;ÜïO$ UéÍ„SgÒ.Æ´/ +,õÀëÊ-²x…]'ñ7VY9¼&çTºŽ¨Â<0™+k)mBú>¦ðéW;.ºù®«7üxkê›øâÃ.?æƇS¿ÁË¿Q8ræð©÷nMíä5lþÆ“UEÏoX´gîö*+¸p¸֒sÛEmØÒ1W­B|˜ ûþȯâߨ¼±>³$‹Á®ªKÇ÷‘3‡¤lT©ÏGm¨+kÚ”µ =ÄŸýž~íÖ_Ò£“Y˲<Þ,++‹-Š­Ò¸=VŸî¿:0=²˜_šu÷vïC샞‡âOã'˜§­·<>äGQÓoF9²l°·¸Žª.ƒŠëLƒD%ûKp²Yh\Ž +Éše4;ThKXÒHo¤HbþlwÑ%L4œÓö’r›ˆL¥¢ &Λ_ÜN,$BšðC"¦¹îŸ\œúé§R¿úö.<ò'¿Åe½Põ“oîøÃìån~ü?fÈ_Îý¯øåxÚž¯Ú~Ïc©¿Ü}8õñmÏÙó0𞙀ÑÌÝN¹GJ.vZfÂ@tYÆÙÔÉ'S¤’ºJ¦9õ(KŠfg™ÿmÔûGõ>Ï ^⫨—N7}‰rC*F®q†²1Q$^â$Nˆ„£aFP ÄEÐôY!Ɔr°×§°ÏÁAÅÊA0‹Éd)ü6â&‚¡¡`(æ&øYS™ö”‚-šó0þç÷gÞÐØÕ9qíÝÇnNíÁuwoÈè ÷/›¸3õ(uÅ5©×>•Jí˜W¹sèÑ?ùá?J0êÇ€3|ó¨¢{€À'$IË‘‰Tä„Š$‘`G–é­§²—ÛŠ­3JTçäÿrÕ.šå"PzÒ&P‚mšpædò«t:¤FÈIqùçf“çßb7ñ‡v¦žMé; jÏÝ cÑN’Žá.ÿ™aÀ¾c3¶Ê0Qõ¿ÑoGuW­ÒD˜ú—î+ÍÐýý?éÍDwýjߟfß;ÿ³»w2é÷ð½ ¡/@×7B¯YôÚ~ÒK†,šìv1]<ÙWUí^U¸×â÷šWà^³î5u[Ju³Úæ·ñ»x–µA,Þ…¶£Ýˆ+GšŒÞG§ïµ!sby×CI†NOß3ÓðifÎ:¦+Sé4<ƽÝ8ÍGΞ±wΦÆöŽúÞ´`J&‰ë’ ºÊzá'DÁ‰Ü)¥+þË 3›à‘DôÒæ)Çö¿Fgÿ…ô„!½›\üq;‘¸ï'Ì/¡#Û M<€`@OLædÆ“)Á()°%nQ…† ž,“”æ%yCce„IV=H’EHU“ôQ…> ¥T÷pz$ŸgFr¾û‚åOâth8rÄ<~üñ '“”c&Qf94[¤3.Ð3KÏ=óô,våä‘C”Ȭ0ž/µ+…žÅŒò%‘ ˦Îk¶â­6è‰×X„=@ÞÐ98©&h%‡™éÈ s5ÝÑÓ” d¦ŸV‹ˆ #y¦ˆ€®!×»ƒirGCˆžcÎzÄ’Ÿ‰IÜ*m³ös˜Jmœ6Î`K¸½Ì3ƒÅ­ÒW{¶è’ÊðR>Ô3‰ÏŽi‚~©Gy€y½O¼Ozš}J¼ŒáñTðŒŸç ,ò +^‚¤¤]e\…Pç$IVTÀlÇ$pšëÝàe¼‡˜§‘Ž‡ìåm©qMVlG[¯bõ ÒƒUxÂô€(€ˆF›‰Ífús6?—ßÀ±0Oï³.옦ú0àÕó í¿9ÙZ_C= ÉüEA$Úß–ë©ò°¾Tò~ˆ´¾s€ƒoƒ"ý6ÕñÆïÖàY1<#>þÏ÷x’›vù¿y §ÎS–CÝþjë<•µ4¹ä¦]ûÉFÐQ{±–ýq04´çXyÎÃÖ8ϪFjðÌNMß•šÁ:÷ÙÝ—Mþ6{þ‹1Ü«çj¸ç1~$k6á¦øú=^•. +ûÕRX RÛGNII fÛ¢ +·Äˆ,+ÉÃȢı6X§ŽÂt¥¬†w) D¨¥èÜd«Ø“n®Ú¦nPyUÎLW uhì¿Ç¢¹4‹þW©¬\4€]%›’„9€’î.²Ð8¦†zjÅÎQ¹äGâ)N<§YÕ’ 'ÀàÆ!D º%gL ÿÈ1u’Sé&+ëÄܾ8d¥›$¹ynL†šW'züpøÈý™>Hf¹É,HHòó=4ü’xé«0°1€Ýw^b™C/OÀ6rëXÎmHë@\/@JGa´×)k¶–ú™ñæxÿ,s–ŸSµP +…]è-”¨Ö-™i>rƉ‘i“¢vÿhXÿŸŠÆ•ì‘,9­‰·7¹ºx¿p¤*ѹ©B“uÉɱ ݯË0%÷LXvO㧩—S·à랸éŠ!›R·ò‡<Þæ˧z{ŸeñÖõ³o +èDúÏîû#÷'С+˜€S4ŸÏu²]WPTÃÖÅG²ãÄ+²FgÊS4…mgg]]|«Ï“GŒT2ÎüL¢ “(Ì$Š2‰<:na7QIfEDbŽ!©b½0ŸÉg‹ +†Õy£ +F—Ï´§çM+X¦.Ñ—zú›ÃkÔµúZãzse~gÁfö6õVý6ãóæü› +îÑï3î $Ò’`PN¡7V• Kp!B%Q/W9¤5ØõAkb·Æ˜XAP”(*À|'ðt­ëÄ 9‘²T#O‚ÓGúÒDWmËO¹1gPA¾GWùЛc’(p,#à‚ü\È%*6(êp߸q*ˆQ+R±‰m<ÏÅmxpÞíø‘&IÓÐãËåBT‚Kˆ'Óãa¦•®éä½’h%Œ z { ¼äòöñÞ©#CæÏJûbNóÍ@´>¯hÄP864P¨…(g “Ÿ`9Э&_f/ëÃÐü>¯7 …Þ¾ú}< +Ûäªy-ruf@t#ž„7#?Æ(ÌKR"öa¯&ˉ€’^K3 Û´ü¦iyeM +xÃ25Ä@—x6l†,K} +{½–…¤h(5GÈøJd# Î8Äã+ØÄ%‰ôàÛ÷<íòˆhdB/¨b½ÑHoxâèæQ¦ã”úU1”Wθß& TÌ.¼±lñ˜G©þh&5ðÀ6ØÁ ¯BŽ\ (€ÌÒ/1 ­ìy gŸæðÎ0):š!|.Bø¼pñU‚Fœy?œºî¥÷ó£Ãúä—“òâƒ>üijÅáÔ«EbÈŸz™?t¾áþ{ÿ”Ïþ®7šúóßnïfÊ@ÓV»yì¹ÇA0\Þ÷ç.AŨ–ä”ɺ\Ñ£¥%zi)(ÖÚØðÒq¥MzSé½¥tnÅmúæ’‡‚ߎîÐÅ·Sâ$©'#ψ.>y½ø—÷Š¥QAœ ¼Ü"ŒÆëýÒ•[CV§‘Tv(;œ,+­®ãêÊÆq—•M—“ ¥–ä*m‹ö²öOýŸI«¶Úƒ9³<¿:T™ãÏ)i-aJâåžÏ]žG<}þÏ.Ï_<¬GKÇK’‰ >ãHŸ‡®Äy²RçñÄÙPóÌð½þx\D¤P”²ÁÑEJeœUKæ™ó@9dAN>;i¡ÿgW+ÍçÍ'Ë»d½8Ÿ¬ü‘±Câ·„ãBŠ6”Ÿá¯ù=Ì,ÇSä¸*»°¢pW!_¼ˆJ3PÞ>@Cê¨"›È«®¨;RÇl¯Ãu!Ò·¤ÆPA8·<ÿáuÉFðP×)]¤ÂÔuJ¯ªf ê:¥ Âa‚&A&A©MÒÈ…Œ34Ýäy2™ ÙÊ”owLè¢v;BAíî*Œµô¯¦ºÈ H¹„¡’2øƒ¡¼BV=Œ»Ê…Øú—ìz~lçe5Kß]„«Fß²~MÖîðŠã·ÞòÌdSå>]s´uvåò–ÅfÝ4mÌ÷ož¸q¢ß£Gó ”ƒ.nl·ß>Þ™wùàÕ§ÏÝ|ñ0ü^qÜ,žP~ÙÜY“.¾0z3`4±EHìñçۘ׌|¾†Íó Ù»³™ììÜxUüÒx[ö¶la¸¯>X½"xE´IjÒgMÁoD—HËôÅÆŠàŠè‘ìw´wCïFþÓ÷çП#È:‘Ý—±ùr£Ü_Á7…1™_È¿›õwî S3N`P,D©â5œ\Ŧꀽ²Aå\¿¢JqT §Ýg©LVI\œš gVIP ÉqÊ <Õ.ÐìçZ1T\W± sƒFµïƧ1—ð$Ìb"Ð ÒbfEÐ STÁÔ?ƒ½U0ELŒs‚a´h4ä]LÝï8’[;ÐqE±¢£~‚Ù 9 L}™Iu,øG}äî2W{jÏÉs4'PÙM”—[Ä‚âôåjý §º;ö\³«ÝI}öÃç—2ÕÓî^õì÷V®z–?Ôû÷»&ÝõJgê/©·¿‹ï{aÚíÇ^=þâ1v“û>bO¿Šâ™i £Ú³ÞÀ†Š‰ÓŠ|õ‚óÆU1çTì ˆ½HG/Ò5qÑ$£)†{óEW3<ÚTI¢ˆŒ•5œéšâ›šë›ú6ómö!ý ó‰¨&ée ÓÂ.áWjmúýIm¿|@Ù¯iAm³ö†õäÎ1Zõk``1Κ +êI› ÝÚ†¶£è4(H†¡¢/û‡®ç{$ÊŸrc0¾|5™ “¥$ ‡Bç2 +“(…ɸx ÿug‹ "#zH!Q!…DÊ^Å!±ê£i  âSÇø)yã©”ÁDÊœê8“<Õ‘‰.±êÊͦ“ðêÁ·Frƒ“Ò ”—@Ž­ß“õ—¼›úGÇÇ·îümö®Èú™·<óĦ%wâ›CϽŽ³°ò,f6îz4¶tÙOßxû'7›s Àì}w% OsžPN/ЫõQ:_㯉_ÍLU®òO‰/bðÍò|ÿÜø‘ì7ù·|ïE>ð}àÿKèO‘(å³³“QB®ã£„vÅÁL¾>88œ©ÑÇ3£õ1þqñ«•éú"ýáÁ/ð‰¬G5  HU´$«†«HÄ„Q`šÇ-lZŽ5×Ú`iœp Ôòʱ¨Ð"¤j ƒ,J°uM·Ï»<:/¾ZX8Ëœ ›(ˆ =šœl ²Á¸±ÍÜn2¦ÉÅâ +ùžÏ3c3ÜìPÌ» Ôq¯¨'äè u©9£gÂku"ÿɔꤼ\TZ½[Çz4›8¾ +«ÉÕAÄl6ÎV™ù¢“_Z”=Rq +)—ÀâF4@j OlJNè=9Ñûñ,µ!'¸)ÀÝ”úÞöútLGzÙHÐŽ ‰™¨ªY~1‡F1á%(°ß8TöéÁSÁþß¾…=øüGÊÞ›çoí}—¹R6ýÖu;ðôÐãÝ8˜½†‹S¿KýÓ´wZŒï݇ pˆeîÁÔÙ¶oÈ°jêtKƳ«·!q™DÈù©ÅYL-Î\B8¨,mk~–vùÓŽ O¨Ø¡îoº!¤§ï ‰GžÇ‡P:‹N&Ï&ñÅål¯SMÄ­§ûêÈ‘k¿i ²(H !™²7†,Áˆá$N–n܈“@'UV^MUMu-1æ­® QÍ{yĽiÕ³cÃ*¯õúëìC[Û—V¹Úû]eÌÜk¶ž_qiêJö ÖêÌUUÞ_¦ø¯PGû9+’U¦úËòêÔ¡þËÕ1þéâ u±ú…ò÷€gp^YÑ%y—]Q´­l{™84ghICÙuLÎè’©9SKZÄù9óKæ–m({·è£œOóþRd…‚B ‡ÙÓ]÷‰T’˜6ª rd:‚Ž#P[™ëJ>7”ѹqM ª +ª”‚pøx›!'47´!Ä•Á”3ÓÊ([ Q¶êgk!ÊÖHH+ÍýÄek¤ qM³µQ +.§Q¯].@¹Ùù/¯ï}—m4“@ÐQŠ1¢¶F. ³Œ“šÜðlƒò6#’,ëÊ!ì-9q{;sÊü +‡ë=y–DBŸLtt÷2¶ƒP +‘ELª@¹q\„Ï…jª,ê)¸p—Z9²ëú[¼j÷oN¯øÅϯ}²ù7ÛôɃO^¿îékW?=#zeAå‚™µ»oÇõï=€ñÖ6œ_òùë«¿Ï–þâÈ ¯ýôÅŸ»z B,YåôãyQ?ª¦»-¨z]ÀÕ°£ÙC:G³†‡"Õ!ÉÒ,?ËcdÄyѯ*ZìT ­î“ñ©Œ :tY¹˜žý21,,ºÀLu;9JÊÉÄ7HA"û Hd"`hÐ3Y’¦÷gÐœ‰A[=´zwðti nîö¹ ã/pã&ôá4Ùhæœ@õ¦¶_8!J¥®Z)‘¦û]ä_¸ú b(Y2Tåœ;y€·–îâ~òä ‘fÓ}‚T$f4¥Nà <‚út‰ˆëz#¢ÆÉ*WK VžEÁ(¬-Ý7YõƒñÝ+—N¾£TÂÏîizâ;½s˜G·\7åÎë{MÞ€ª§ë¡":æ|CJF0IÞ&o—wËGä÷åÓ²ˆäl¹MÞ ?’Î:!÷ÉJ¶ :–È1¬,°7`$ð§b¸G¸íÜnîw‚Žp§9q6wî8ÎÕ•™i\ÿ¼qtÞ8…´ÊQÎÆe8—ñªq„ˆ2‡ÜD髳×AwH“™¸7º©£=Icê`Vnéîîæþôúëç\á¹wIÕc©+ñp:f/zËÍñüE\¿™çCÏ‹Çp¼a]eX¿ÆY¼*’ª‚·ŒmÀÑC! J½@Q¶©8[mP'©,YtjÉˆÒ ”ÔPP©M©&¨e¢‘A©µI(m«ŸgÎØTM©lƒú‰&q©´£† Ä&€QyëúÇgUUm1%7ÂÁ#™F¡d*1,{Är1‚l|­ +`w?ÌÝÕ°¹;µ8whvíÐîª÷ã>þÅ/þy݃žq÷p³Ïm?:a¡WÀös•ÀÌsb‚«[ Ó…™2kèãÏ +¬œ =:C­.%“3 ºRE÷$Nc¯U¯`ûrª%0Îöy‹ªeb¤ÁÕËÓŒšál‚ãxN¨•Ç(„AÊ åZv¥ò.ûA|RÀyB¡X Õ Ãä}’ÞÈ5 +3ÄFùzn ÿ ü¢ðKîmá¤ð±øáŸRÀ«(<ËrŒ ˆ²,Á,I¢àEå¸^ñó¼¢ÂrÄùÉñÄ妪Ház°áÈ^ÆÙò&–‘Ã:Y<ÛÃݱä(rYV,eeÕ $â(«.oîµéeONz_]¹lGÉ$]ðúŽìÍ¡‹l{ƒäò»½fà^èF/{ÔÌÊ'Y9#Myßã°äBk~==Á[g÷†ÉËÞ«K/ +4ºÞ²<@ùÙôœ‡E PüÌÇ©%ø…ߥ]Ï:ÿ<ÞZÕ»€É^›šEðò&8ÕRzýÞ2(„Q;Ì Æ¨®q¯CÜ«û†#NˆƒÏæáßç¹Ip:ͳÙ|¿ïã9àæ +ú žÔD}4›G>f&3Ûþ%·ÏÀí]X»ú˜”VÆ2 }}™¥4ïB¹ ya^Äuäp`zG~dfnꦡ®  +AgÊÃ/‘é3™ø‚3™½Ú¿v&¨zuw’;)ÿ>ôÍ¿ÅŸµ™dçÉá˜-³l^".ˆJ!b!/1•ãx[Áö¦ø˜§`›…-ŽZlaj­Q7µØü4žîÞ%µj·Q6fQ•Y´2 +Vnr´pÁ¶ŽÑêbýÕÅhu1åa‘êbTJƨá#´D…sL#Ç2ž¿©/ˆ˜ª¼|aâ`²¡?–Ò_Ö¿Ðå¸(˜–Àç3:òÇOE± +K’ù=xõ¾¯r`×?Ó{r€Ëf€«nz©»»¬PžAôP"¶èfÛºŒ Öü¾B¿fÅ°WduÚt!»éRXˆîË âšêÑ÷£•O.Yuö ¯<ü̾¼Ù—´}«{Æ‚+6ç +ï8皇vè-b¾»lÎð{Ÿè½ŸÙ»zõä‡îî}'£s}øÄ×;>ž|ÌÓfùö¾ÓìYŸÀ–[³ÆĘÇÃ'Â}aΖüÐ :‚º¢{4O~˜êYaªs©TÛR©¶¥ök[*%5—– 3Lµ-•j[pÿO ª’öÆu(;T©B§bø§N ¢‹Í+|:Ì´…·‡w‡„¹0ËT‚”6Ïv[V:$ék.å+ +—5@áâÒ”xÄñ~U›¢[ú@…g¨vA.üÜUÔܯ…KV$ETXÁ,´O Š7 d2ØN¸0…rÚ‹;Ä[[ùÞÜG'›JwéÒË:Ÿâ +ïß5ºmBåõ½ÌæËGÜóZ/³Õ÷WPÔQ/= ;Œ}dµ€Ú„$;I*BxE%¢.“¦ Ò"¡E’ªÍáÞáÁšðhs¼w|ptx6?[¾Êlò6¯ +/ç—Ë ÌåÞåÁákq@x};•ŸªÌÒ–±Í|³²LSBqN´€eøócÔö‰Q4û?I RgNÚ˜q½ÒDz?¡»I‹&ÒQG_~Au…ˆ‘hŠ¶ÈŠCÞAòÇW¤=ùHó³—Æ3#êkDq +_êBHS-å?ˆî'DTI؃†D‰K!ý)rf{²élSÓXf¾Aü=DlÉSø)ò5ü52Gd)⣛ Qzká@£hÔ·þì78xÝŸn?uêàÞ-›÷î»yË^Ƈ‹î\•ú}ï±?݈XíÕ×~ñ³W_mIµp9A/Jàkœ;5sy±9ÞäìÝ6“m—hyY•Ê¬K³Úìm¶4<4Ö> ›%L®– Ô15Ú8fŒ6“iaÞÑ~þCðãȇ±óŒ9Ý«¢GðÇ9\ÈS…ˆÕÀ¦ás — Žˆ…žAF¿# Žƒ:" *H©+!Hæšîá¤R¼r.ë_ý«ù”’©B¤>1è*¾®¿.+q¡÷ák|«½gêÿ0¨[i?øд»á¯jYéýÓ~˜úKë7ü¬ý±ÞœgWw>¹kÕÊÇS-ŒtÑD<‹ÛS7=yç#ÙÇŽýô¥7ß~‰H¸›4/T,ô²sQ¹›Î㪹‘Ün!×Å ²%É’¬û,YG¬„UJH‘‹·IXʵ}ØÇäZÿÞ²ï×õ>w¬‚F ŒèÂ5î…JþDïØ£ÿbÜŸ4›Ît(e25u™ óå-œØÔA¢Ì]ôu=j"Š›»¤¥aÖ7.¹ôÒ‹¾áOp…¶_6ü©¢± s;zß$³ÐÐ÷»f¡‚ 9×q¹þÜáòåò¨üé¹Í¹ëä;åMùOú¾_öV—CÑp¨b|ÙÛ!>ÆLc³+áÙÒly¶2[­ÍÖ—HKä%Êu‰¶Dï.ì.2H¸N~ÉÐü™J£º pAqW^Wþ†üo*ßÑî)¾¿ìÞŠ'”ÚãEOï+üYa°8£‰æfy™D~&QìZ‡é2$‘—IägY$nÏ›¨›)h +µ œ:8+J\w¹‘2ººiˆLŠÌ‰ìŠ¼ŒHv¤5ò~„ËŽÜa"?Ø/¨¯Ûñ“â& N5ñq0ô°‰É#ûüÁj×1<;kY“ˆœ»Mfœ:>`.>XÍŽâh~Äñ…«+ÉëåÔ_vÏ„Z"ô›K›¼±É[j8F¨¿;ÒÃÌÚ+æ—«ûãuÇKq)i…¼QšùŒSi†N!ñ Ýë\¥Må•VÏ­…]}—¢œíÎ2°v’ °éŽlÒ ;ß  Ø Ý3ì4‡ø±)ß ñËi7cîû³62$íœ"O³bò.ÓKßÉdû€}>Iw%,I>žÓN—¾‰-CÅÈÅ]ÿN/ƒöä Jäñþ²BËôš>“ru;†äb1†ùApJøá6Ç“C¹yº&•(1\\$+B’‹¡l3‹èYäCZõî‰Æn–&7n܈°#âÿiêÿÈCQaÑ`¦¦zhí¿ Á‰„¤І½Æ­×­[]SðÍœ4bXéÝS®ÿáLk·ÖÙ²nI0XÛôÂýÓ[^¼þõwðÅñ¥Í£.Î TŽÛ8qìšâìäe×- +_5ûªÚ¼x–Oɯ±nöÌG®~–Ði~ßgL)ÿ +¡_D +ÙJPXM?þ7"aMW0‹‚¦œ4ݬj˜¹(ëÞ ÷‰Òhyô\±MÜ n9šÓvq·xD<. +"Ö„W‰®°¦‰ÏhPD:= \ÝÕ ]ŒÈ~âÚI«f®V)b– 0ºgáWŒTúɶÞzó$áð§HüáðVUݽ +úUAÈ]:#+V-ýÆ ÝQΘÑ+ê¯YV¶iÓ¾ýû}ÉâÄ£˜—4?ÆÌߊÅe©;¶ö~sBY”Ú÷ÀËNp…Ðú¤ƒ(JÖœÀrgl_°š|ŒÀ©òú«“>œ/ù‚öU`æLª +„CÄœˆR[%D­”—ºåûƒMB”}‡úí“?í O{ƒCÔà ûD'óÑÂGB841JýÄ4‰žŽ2mÑíÑÝѾ(Õ +ä~ÁA¾BfËÇå2'g‡Ü/8ÒÞh…ú IýT^ÈÔ6‘©3Xž¹À%@œ¾ÿj„Ô÷Ò5ˆúÌX ¢(gztC'1cd{"œCºd¹.ÀÒÒ áÝôªfQ!u†(AP— Û°î­o<>ÉT»UkÅ•WÞyQ÷wº/[>©¦“¹§wßCÆ^9å®[˜ºsït¢Ä‹ÐQð'éx/!E°Ð˜OãÎË“ãiXâs5˜ZÁönZÔ:us ñ¥c$ Ä?Øáå²2;|¼D3¥ÜÇ Ò6Æ›ô^«%)ËÖÜGº=V’²z¯X,FhCX0`6tnqÕ,Ìp +g)iï”Ëè,òAcæÛÇÌ7éfŸt„%ùbˆúq)W¢0—[³¬;-Ö²ÝÏI¥?ŒÃea;rvNµÏrýÖÎsÙùÕœ É>!&G¼<‡8A•Uä5‘õ‹q)¦f[ –JIO5ª‡KyF±cGœ WGc­Ë½³Œ«¼KÅÒ"ïa­Ø%¼ÎÉŪUŒŠõ"O±Qä-÷CµÞk¥ÍÒìýÚSøiæiõIm?: òüœ{[xGþˆûÈø£÷Œð…WÒcžMÁ Ó£"ž½i´)ƒó"K¥Ñ(ð3Î#²:Ö +ôž¾·ZÂ¥tÀ¾Rj«éØïÕ*T’ÖTî*e¶µÌZgÝf)–Â.p¸€ùj0kyòL¹nž$®ô‡1ÇÏÒ W‘—EE1- øûø}<ò‚Î2ÎY¨û§–(Ù¢åõ&yÑÏó¢à\ {üºî‘ÀÜI*’^'‘¯iJA ½œdXšG§Ýó'û) éx ²CñŸ5uâêoÌïIwS ÝE +å'¾ ½e‹X<>uøÐŽ®jÇÁGj.>°+Õ}xGɯ€Á|û¤õ +³¢÷W1 ϽˬÛþuà4È¡¿§1ñoÓr(``UàY`0Ò ¹Qž¤HI÷EÇž3¼ØÈÐU gr¤n¦qwŸô ç!ãD8"¾jȆ¬‹²>9 GÍ<\݈ïT¥rïÕ\£Ø¨ÎðÜPPŸcz´Ÿ«¯x^3ßeß’¡ÿÆü@ñfˆKÕ×2Â:(án’2ÄèHQn|"(lÈ ø^(¬(É2™çXPù ç:6 ÝTA©`t•ÕLE0C1_D/ÊŒY€d?B2Ëè/êX/ÐX¿¦±Š,³,#€% iH™äÅÞqú Z®bÌä$ÃsŽ0YØ@?Á0ÒñØì Lî$˜ËqÖº£éïFRa²ÂüÀÿÅ÷6_teÉØàœ)îÿ÷G}Ed/ù×üFÒÿCŽCt»¨¿‚â’ÒdÙ ÁåC*«ªk†Ö«~QýÅ—48ÿ÷m÷ÿFüʒߥÿ“ÂÑT¶x_a8ûøól :ÖìMfe‘›µ÷¢l§‡ÍÛç T#±dŸ[9=Ûpn…c/ÀÁ¡9,ùR… çõpl€c/Àq˜k8“§6­p<Ç ò„Íbã{ílsDwÉÿ¾h°!ô8úàø¿»ð¨Šl}ªêö’4B–Núv:i%ðÂfÒIº‘¦™ p *B3Š@³Ïapñ©7Ìk‚>PTFpÔqu\Þ è÷©ã–ûþºÝ‰džï}Ó•¿Î©sNmçž[·ªI¸‚ÈK€ÉÀl` °0vR²X.¯Ù6cÙ`Ž…וÅæX±i¦Qì¸:£“êcÔwyÌllÌløȘxhUŒ^R£i¥!ISJW¦‹tLR¾Öq1rÆŸ§Tì4t¿DÀ…9.ñŠ´Ž|wéîCB!&8,óÈ¡,’Ò¿´2‘ëüBZÉ»ÐáuW#U™¹Ç^Ùnu·›{ÝÛw (3÷æmàdæ¾s#8™¹o] Nfîën'3÷¼…àdæž1œÌÜ“À!‹ò]ÿ™‰£lòµL­LåËà¥eðÒ2xi)|™Lô½"Çvo¤°Ûéõ )t„ºXèišÂB²P ­d¡Õ,4ž…f±‡…ì,”ËB^:ÈFÃ!æÝߧ8Æ›ÁBÇXèqjc!7 °P> ©¬ÌåÎÈå# â7HG¥¼é@/+Çê“Êð¨1ïÄšpùI@7J^©y1ãÌ\Ió: ++bå¡cKUNäGPñ.Ã: (¸@GFGÐÈ4Š¼˜ Î:`†u¾ÅÈS‘—Àl`p0Ã9pZâ“ÆÀJ⃞,KüR’“;½96»Íc›(¶`ߘË&çê¹¼ŒÒÓ±"§õ·ö²”ÎïRþñ] +%T&ðÍ| Š¾5N·D¾ÏqDÙ=÷AGå öÊUul ¹Yèhj3Ê£Èn•t$Ùùc ¥ûtTK¸‹]¬Ÿ¬ÕéøÞþ±ãsìðÀ~f?èxK*,âø+$u:Þ°¯w¼TµBò´;Ê@ºTÃô€}´ãñc†éj(vF+%étÜnŸà¸Ön(ZbŠYm(ySSÜ3ÑžÏ>ÇámC›Ž +û,Çø˜Õ(Y§Ó1 CðÄØB vˆÝèÔ•k48­,ÊZ½E–í–FËdË¿YJ-E§Åaɱd[ZÓ¬6k?k²5Ñjµš­Š•[É:Pþª°G>õšmÆ×OŠÌƒ·q™Ë­‹ñ­€•c#¡ µ¼vj«ÕÏ¥Ú9ªöíTW”%ÖÏÐL®*¦¥ÕRmC•6ÚSµèS´2O­f©ûMc;c›j|]”QCc”éR´&[K«–Äú¯Ù”-é¥k6”‘~sEFEZyÿ15¾_É‚ñü¢¯Ø2úð9ÚöÚ©Ú¾œ€V*=Gb×˾fü¾ì+ID9ûÚ?EÊE¹/¨²é†v‚_Áó•agŃYÚ‘jÍÙíŒÙ >ìò%]Bv †Â¤]{[¾ßמŸoØ V©Í°i¬^ls¬6†MzˆŽ6ÇÒCÒF+7Lìv˜äÚ –EvÃÄβ “鿘”ÄMÖ÷š¬7zì{Ì&åLMÊØxþÕOKΗãs›ü-.Ðåo‚Ú†›[3´ÐUmŸ +Uîàœ¹­’6·hW‹O›ëò©íãš~EÝ$Õã\¾vjò74¶7y[|‘qÞq~W³/Ð1¡ndYŸ¾Ö÷ö5²îW«“”}M(ûu™TO}•É¾Êd_¼Œ¾ÈˆñºÆv+UpZ2hOJD¼³ªtÛâr#xÇ93Vfwa·²—’<-ÙU¥¥RU\Y\)U¸§¤ªÄ©qUÆÊqÎì.¶7®²AÜßUEž%KÛ–R†/öÓ†DK–J‡ÇrOÛÿõίy›}òýíµZ!Î~8ûµ[,å”´±=²¤$T?…p¬ +Ñk(eã¥,!!nø¿¯ÿÒ85Î’!~°ƒáLµ„ÚBË­màX +f`®M3»°—’‡¶&ØÆ<¬­§ø°{þŸ%É9÷`ÉÒ8÷Å’8ÕD•¶—ô~¤³<½[‚coƒ¯¦¯i<ý‘Œ7©ã¬4G–V%‡LÆò6éZ:DgY[Ê>ÁãÇÇçðb¹ø›ò£é3ó=–¥Ö µG³¬¤‰I{’÷¤¬è7±ß'©ëSÿÛVc‹Ú~NÛ6`þ€/U Úž^7xQÆÛ™G³ȾÍ>#GÉù)÷„c¢ã §êÜ•×éÚ‘kA“û÷¾KW± 9]øe±k訒撵%ïÈqZÊ»¯¢jýðä·ÚzÞcÿ˧Ú[=­¡Ò[Q~ÙøqcÇŒ.5rDéða%C‹‹<…C.½Ä]ïÊsªŽÜ{vV¦|Ö@ãEXýR’“Œÿ¢CpFE~WMPÕÜAMq»&N,–eW3Í ‚š +QM_M fj_K/,¯ù'KoÌÒÛkÉlêx_\¤ú]ªvÜçR£lF}#øM>W@ÕÎü$ƒßjð)àNTPý­>UcAÕ¯ÕÜÜö}h®=)±ÚUÝ’X\Dí‰I`“Àiƒ]‹ÛÙàrf0|°l;'k +¥e¹|~-Óå“#ÐD¿yžVWßè÷e;â"UÏuÍÑHÞ¡Īn4sµf1ºQÈÙе½èpxcÔFs‚žäy®yÍMšhÈ>ú{ЯO|ëÇ¿Ñ8nøµk³EØŸ±@•Åpx­ªÝ_ßx±Ö)ó@m ./¨ †kÐõF8±V.Þ_hÔØt©Ê™ÈYÅæ[â +‚ U-ÁUåj / âÒd…5šr‹3’•å= Ÿ¡,¿nht9µŠlW ÙgoHá)·tdzÕ̾šâ¢v[ÿ˜cÛû¥Æ™ä”‹™–^Áæ’«ÒëY&G亡©sUŒ¤Ñ…9–YËh +Ï 3| µ´y¸" ´„ê`Ø6VÊe}ÍT`s©áoà:÷÷¾’æ¸Ä\`û†$+ã¤7Ô ïá5G+,”!b©Æ5ÅËò¨â¢›£ÜåZlþMºêàÛæÀظßé”xCÔKsPÐBõ±²Js²#ä-ÁÂ̓Rs¸G3hšÔ„z4½Õƒ.Dò~ㆤYݽ?©¶ôþÖ±KÿÔ-1}ü¦U8÷mmCŸRL?ºWç´Õ"›Ç9ž- mleî1A¡1YS +ðc6‚z^ÔbET¦Öh¶àÄXHt:ÿÅJQý‚¬e_ªÅ‡©õô-ëSî3¼ä°À€7Ç%Nì£C¨Å:¼ÀŸãÏ…ûƒ=Õ»6dk5ñH¶²±¸)8Uµ»Øºúv/[7uFã¬ëê:ùNÆ«ƒUéHùõÅWϸ%ÅD¦.ʲL{(Sþ>‡|kð™¤Ý tù·€™’ò/9Ñ8ˆöÒãl=Ž'Úsìj=Ih?Éã»î£t7­Å‘l$ëi +’ ò»Y¦¾OÆp({€ŽÃöjZI]”Î2ôÏi­¯£ÖJ¡<ª¤:ZD›Ø•úRj¢ÓÊTFWÒ ´˜…ôF}³¾Mÿ=LÄŸõŸ)‰²h.ÒqýKÓÛúûTŒ¿§tšmKxŠ¼è%Ë?ÒM´SÌT˜>_ÿ#pÒ2ŒA¡Itœæ´ÞBŸ² ¶BT£•‡tMVvšI­´“ºØ(6;MMú$ý8¥£åhuE¨)JÏл,ÙtAÿ“~2©ˆ.Ç|öÓ vXtÿ¼º»‚äëTå«©Æ@³ˆþ‹ŽÒ«ÌÅžå‹LɦR“×t«þ ¤á4 £Ýƒšcßñ•H«Ä‹J^%ÿ×{úô6½@²,VÂ&³é|_Äw‰›ÈŠ‡#É—4¯§{Ðú)l:y2?)RS~4çtŸÑûኸé^ìQže)˜©ÊÚØoÙ›ì#^Ígó{ùYq·ò¨òš¥³žE×Ó&zŒ¾cil4«g¿a­l[Ë~Çv°ãìUö¯ä üZ~^´ŠÅ3JÒT¥M¹Ãt—iƒù³îÆîç»ÿÒý^ªßEõˆ‡Õýïifv€NÒ;H§±2±$ÖIeN6݆´’mb²½ìQ¶½¼ÊβÏqœú†ýȱâfž“<¾»øM|¿›ßÇO"½ÊÿοƒEžðˆQb¼ˆEÕZ±é)ñ¡’¥œTtø¹Ô´Ý´Û´×ô˜é9Ós²å·8Ÿ¾òÓC?þ|ª›º×uoïŽtï×?Ä^M~n'vtõÔŒ´×{;"îIz%ÃwY¬•³+á™Ùl!»‘-‡'ïd;ÙÃÆØŸ`OÃKo±ós +·cÊGñ*>ioá7ò­|ßÏßä?‹H©b(ÄLÑ"–ˆ[Äv¡‰WÄâ¬øVü„¤+‰ŠCÉSÜŠG™ ÌV–*»”O•OMM¦—MŸ˜Í×›ï2GÍ_áD^n©³Ô[fZ¶X:-oXƒˆÎ#ô”ñ½{…_NLbµl*-äÃc­™*û@Æ+Gèœò4æv-/7'³•ü¼9™"8ßAŸ/ˆaŠG¼LïŠÓÌ¢<@ï)‰l0;Ç÷ˆ:DÁ3J¹©‘œâ>zBÜÈn§§¸Ÿ(ñGëFÄñUlÖ…VÊþ!tü*DQ™øˆî kùÛt÷ñ:ú›§Ì§Í4‚­ OéÜCL7˜ ̓ØK|æØ~âÊ£˜Ý–Ï„i ÝÉfŠæóüZJ'•D:%þ£?ÉŸ“” ¦)¬wÀítݨ¯¦[LÊkl> 6 +”3XÝVˆRÅ º +«JÖ´NÜÝ]X*Å$H29W".¦a…؉tÖ ´÷øÕXÅNÐ~sÒ|S?†U'…—»§Ð ýڡϧômTŒõ`­¾-î¥Oh íekºo£Å”‹;ç»ÒTÃOšjôbæïð©|{ßë o° úé ÊM)¬¼ES©BߨÿÑ})VØ4‡® 1Ë/ÑÃDq˜Ft_ÅÛõ±ó=MõúÝÁ©U¿Ž&ÓÓô°ÅDÍ®±Æ^Ã|o£>E_"ZºÀ[à/¼µëÏzbÛŠÏjÚiÑdǨø ]ÃÓ¬&.@~àÊBý0-—ÿ6• ÇÝ0©Z¾7LÕ2½Þ]ÏFXÊYÄKL×uøÈyÈ_ûVÌ‹)@ãoÑ,¶–fð}´BBäò}à+A»¤ÒFÓ€ÓÀx`:—Mš©² Û²®ÕA‹LÓõŸMÓi»é(]ìÿ òí…þl·›÷Ñ=ß¹Ô5A7 4Á² ÏÑ6º¸ 'ï:Рõ€VkÙQZÇŽêBJw ­µRøât"Æ¿ú +ÔËGùðYèO~y– +8ùqºu¯û2³b +endstream +endobj +3 0 obj +<> +endobj +15 0 obj +<>/Font<>>>/MediaBox[0 482 361 792]>> +endobj +16 0 obj +<>/Font<>>>/MediaBox[357 490 612 631]>> +endobj +19 0 obj +<> +endobj +17 0 obj +<>stream +0.000 0.000 0.000 rg +0.000 0.000 0.000 RG +q +1.000 0.000 0.000 RG + +q +561.600 0 0 254.991 25.200 501.009 cm +/I1 Do +Q + +BT +380.88 700.456 Td +/F2.0 13 Tf +<373238363438313231382d6d656d62> Tj +ET + +0.000 0.000 0.000 rg + +BT +43.2 681.008 Td +/F2.0 14 Tf +<4d6574726f526f636b> Tj +ET + +0.000 0.000 0.000 rg + +BT +43.2 661.502 Td +/F2.0 10 Tf +<5768617420796f7520676574> Tj +ET + + +BT +43.2 650.632 Td +/F1.0 10 Tf +<31206d6f6e7468206d656d626572736869702c20756e6c696d697465642072656e74616c7320616e6420626567696e6e65727320636c696d62696e6720636c617373> Tj +ET + + +BT +43.2 632.562 Td +/F2.0 10 Tf +<52656465656d61626c652061742074686520666f6c6c6f77696e67206c6f636174696f6e287329> Tj +ET + + +BT +43.2 621.692 Td +/F1.0 10 Tf +<3639204e6f726d616e2053747265657420457665726574742c204d41> Tj +ET + + +BT +43.2 596.422 Td +/F1.0 10 Tf +<20> Tj +ET + + +BT +381.6 665.92 Td +/F2.0 10 Tf +[<5075726368617365642062793a2053616d2054> 74.21875 <6f62696e2d686f63687374616474>] TJ +ET + +0.000 0.000 0.000 rg + +BT +381.6 575.92 Td +/F2.0 10 Tf +<497373756564206279204d6574726f526f636b> Tj +ET + + +BT +381.6 565.05 Td +/F2.0 10 Tf +[<497373756564206f6e20546875205365702032322031393a33303a33302055544320323031> 55.17578125 <31>] TJ +ET + + +BT +381.6 554.18 Td +/F2.0 10 Tf +<4e6f742076616c696420756e74696c3a20467269205365702032332031393a33303a333020555443> Tj +ET + + +BT +381.6 543.31 Td +/F2.0 10 Tf +[<323031> 55.17578125 <31>] TJ +ET + + +BT +381.6 532.44 Td +/F2.0 10 Tf +<50726f6d6f74696f6e616c2076616c756520657870697265733a20546875204d6172203232> Tj +ET + + +BT +381.6 521.57 Td +/F2.0 10 Tf +<30303a30303a3030202d3034303020323031322a> Tj +ET + +0.000 0.000 0.000 rg + +BT +36.0 488.064 Td +/F2.0 12 Tf +<4d6574726f526f636b20284d656d6229> Tj +ET + + +BT +36.0 467.92 Td +/F2.0 10 Tf +<496e737472756374696f6e73> Tj +ET + + +BT +36.0 449.85 Td +/F1.0 10 Tf +<2a20506c656173652063616c6c203631372d3338372d3736323520284576657265747429206f72203937382d3439392d37363235> Tj +ET + + +BT +36.0 438.98 Td +/F1.0 10 Tf +<284e657762757279706f72742920746f207265736572766520636c6173736573206f72206368616c6c656e676520636f757273652074696d652e> Tj +ET + + +BT +36.0 428.11 Td +/F1.0 10 Tf +[<466f722073687574746c6520736572766963652066726f6d207468652057> 18.06640625 <656c6c696e67746f6e20> 18.06640625 <54> 18.06640625 <2073746f702c20706c656173652063616c6c>] TJ +ET + + +BT +36.0 417.24 Td +/F1.0 10 Tf +<3631372d3338372d373632352075706f6e206172726976616c206174207468652073746174696f6e2e> Tj +ET + + +BT +36.0 406.37 Td +/F1.0 10 Tf +<2a205072696e7420766f756368657220616e64206272696e6720746f2061206d65726368616e74206c6f636174696f6e206c6973746564206f6e> Tj +ET + + +BT +36.0 395.5 Td +/F1.0 10 Tf +[<7468697320766f7563686572> 55.17578125 <2e>] TJ +ET + + +BT +36.0 384.63 Td +/F1.0 10 Tf +[<2a2057> 18.06640625 <7269746520796f7572206e616d6520616e642066756c6c206164647265737320666f756e64206f6e20796f757220494420696e20746865>] TJ +ET + + +BT +36.0 373.760000000001 Td +/F1.0 10 Tf +<73706163652070726f76696465642062656c6f77202874686520706572736f6e2072656465656d696e67207468697320766f7563686572> Tj +ET + + +BT +36.0 362.890000000001 Td +/F1.0 10 Tf +<73686f756c642070726f76696465207468697320696e666f726d6174696f6e2c206e6f74206e65636573736172696c7920746865> Tj +ET + + +BT +36.0 352.020000000001 Td +/F1.0 10 Tf +<70757263686173657220696620676976656e20617320612067696674292e20> Tj +ET + + +BT +36.0 341.150000000001 Td +/F1.0 10 Tf +<2a2050726573656e742076616c69642c20676f7665726e6d656e742d6973737565642070686f746f204944207768656e> Tj +ET + + +BT +36.0 330.280000000001 Td +/F1.0 10 Tf +<72656465656d696e6720796f757220766f75636865727320286e616d65206f6e20494420646f6573206e6f74206861766520746f> Tj +ET + + +BT +36.0 319.410000000001 Td +/F1.0 10 Tf +<6d6174636820746865207075726368617365722773206e616d6520696620676976656e20617320612067696674292e> Tj +ET + + +BT +36.0 308.540000000001 Td +ET + + +BT +36.0 297.670000000001 Td +/F1.0 10 Tf +[<496e737472756374696f6e7320666f72206d65726368616e743a2042757957> -0.0 <6974684d652068617320616c726561647920636f6c6c6563746564>] TJ +ET + + +BT +36.0 286.800000000001 Td +/F1.0 10 Tf +<7061796d656e74206f6e20796f757220626568616c662e> Tj +ET + + +BT +306.0 467.92 Td +/F2.0 10 Tf +<44657461696c73> Tj +ET + + +BT +306.0 449.85 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 438.98 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 428.11 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 417.24 Td +/F1.0 10 Tf +<6d656d6265727368697020626567696e732066726f6d20796f75722066697273742076697369743b20706c656173652070726573656e74> Tj +ET + + +BT +306.0 406.37 Td +/F1.0 10 Tf +<766f756368657220746f2061637469766174652e20> Tj +ET + + +BT +306.0 395.5 Td +/F1.0 10 Tf +[ -37.109375 18.06640625 <7320636c6173732e20> 55.17578125 <41> 55.17578125 <20636c617373206973206e6f74>] TJ +ET + + +BT +306.0 384.63 Td +/F1.0 10 Tf +[<6e656365737361727920696620796f7520616c7265616479206b6e6f7720686f7720746f2062656c6179> 74.21875 <2e>] TJ +ET + + +BT +306.0 373.760000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 362.890000000001 Td +/F1.0 10 Tf +<70756e636820636172642e> Tj +ET + + +BT +306.0 352.020000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 341.150000000001 Td +/F1.0 10 Tf +<6c6f636174696f6e206f6e6c793b206d757374206265206174206c6561737420342720362220746f20676f207468726f7567682074686520636f757273652e> Tj +ET + + +BT +306.0 330.280000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 319.410000000001 Td +/F1.0 10 Tf +[<617661696c6162696c697479> 74.21875 <2e>] TJ +ET + + +BT +306.0 308.540000000001 Td +/F1.0 10 Tf +[ 55.17578125 <4167657320313820616e6420756e646572206d7573742068617665206120776169766572207369676e6564206279206120706172656e74>] TJ +ET + + +BT +306.0 297.670000000001 Td +/F1.0 10 Tf +<6f72206c6567616c20677561726469616e2e> Tj +ET + + +BT +306.0 286.800000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 275.930000000001 Td +/F1.0 10 Tf +[<57> 18.06640625 <656c6c696e67746f6e20> 18.06640625 <54> 18.06640625 <2073746f702e>] TJ +ET + + +BT +306.0 265.060000000001 Td +/F1.0 10 Tf +[ 18.06640625 <66657273206f722070726f6d6f74696f6e732e>] TJ +ET + + +BT +306.0 254.190000000001 Td +/F1.0 10 Tf +[ 55.17578125 <6f7563686572206e6f742076616c696420756e74696c20746865206461792061667465722070757263686173652e>] TJ +ET + + +BT +306.0 243.320000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 232.450000000001 Td +/F1.0 10 Tf +<6d757374206265206163746976617465642062792030332f32322f323031322e> Tj +ET + + +BT +306.0 221.580000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 210.710000000001 Td +/F1.0 10 Tf +<30332f32322f323031322e> Tj +ET + + +BT +36.0 138.176 Td +/F2.0 8 Tf +[<4265666f72652072656465656d696e67207468697320766f7563686572> 55.17578125 <2c20706c6561736520777269746520696e207468652072657175657374656420696e666f726d6174696f6e2062656c6f77> 37.109375 <2e2020> 37.109375 <4174207468652074696d65206f6620726564656d7074696f6e2c20796f75206d7573742070726573656e7420612076616c69642c>] TJ +ET + + +BT +36.0 129.48 Td +/F2.0 8 Tf +<676f7665726e6d656e742d6973737565642070686f746f2049442074686174206d617463686573207468697320696e666f726d6174696f6e2e> Tj +ET + +0.749 0.749 0.749 RG +36.000 86.400 216.000 36.000 re +S + +BT +36.0 76.976 Td +/F1.0 8 Tf +<506c6561736520777269746520796f7572206e616d652068657265> Tj +ET + +252.000 86.400 216.000 36.000 re +S + +BT +252.0 76.976 Td +/F1.0 8 Tf +[<46756c6c20> 55.17578125 <41646472657373206173206c6973746564206f6e20796f7572204944>] TJ +ET + +468.000 86.400 108.000 36.000 re +S + +BT +468.0 76.976 Td +/F1.0 8 Tf +<5374617465206c6973746564206f6e20796f7572204944> Tj +ET + + +BT +239.576 66.176 Td +/F1.0 8 Tf +[<5468616e6b20796f7520666f72207573696e672042757957> -0.0 <6974684d652e636f6d21>] TJ +ET + + +BT +151.5 57.48 Td +/F1.0 8 Tf +<496620796f75206e65656420616e7920617373697374616e63652c20796f752063616e20726561636820757320617420696e666f40627579776974686d652e636f6d206f72203836362d3638302d37303038> Tj +ET + + +BT +67.292703125 48.784 Td +/F1.0 8 Tf +[<2a416674657220746869732065787069726174696f6e20646174652c20796f75206d6179207374696c6c2062652061626c6520746f2072656465656d207468697320766f756368657220666f7220746865207072696365206f726967696e616c6c79207061696420666f722069742c206966207265717569726564206279206c6177> 55.17578125 <2e20506c6561736520736565>] TJ +ET + + +BT +220.912 40.088 Td +/F1.0 8 Tf +<687474703a2f2f627579776974686d652e636f6d2f70616765732f66617120666f72206d6f72652064657461696c73> Tj +ET + +Q + +BT +36.0 138.176 Td +/F2.0 8 Tf +[<4265666f72652072656465656d696e67207468697320766f7563686572> 55.17578125 <2c20706c6561736520777269746520696e207468652072657175657374656420696e666f726d6174696f6e2062656c6f77> 37.109375 <2e2020> 37.109375 <4174207468652074696d65206f6620726564656d7074696f6e2c20796f75206d7573742070726573656e7420612076616c69642c>] TJ +ET + + +BT +36.0 129.48 Td +/F2.0 8 Tf +<676f7665726e6d656e742d6973737565642070686f746f2049442074686174206d617463686573207468697320696e666f726d6174696f6e2e> Tj +ET + +0.749 0.749 0.749 RG +36.000 86.400 216.000 36.000 re +S + +BT +36.0 76.976 Td +/F1.0 8 Tf +<506c6561736520777269746520796f7572206e616d652068657265> Tj +ET + +252.000 86.400 216.000 36.000 re +S + +BT +252.0 76.976 Td +/F1.0 8 Tf +[<46756c6c20> 55.17578125 <41646472657373206173206c6973746564206f6e20796f7572204944>] TJ +ET + +468.000 86.400 108.000 36.000 re +S + +BT +468.0 76.976 Td +/F1.0 8 Tf +<5374617465206c6973746564206f6e20796f7572204944> Tj +ET + + +BT +239.576 66.176 Td +/F1.0 8 Tf +[<5468616e6b20796f7520666f72207573696e672042757957> -0.0 <6974684d652e636f6d21>] TJ +ET + + +BT +151.5 57.48 Td +/F1.0 8 Tf +<496620796f75206e65656420616e7920617373697374616e63652c20796f752063616e20726561636820757320617420696e666f40627579776974686d652e636f6d206f72203836362d3638302d37303038> Tj +ET + + +BT +67.292703125 48.784 Td +/F1.0 8 Tf +[<2a416674657220746869732065787069726174696f6e20646174652c20796f75206d6179207374696c6c2062652061626c6520746f2072656465656d207468697320766f756368657220666f7220746865207072696365206f726967696e616c6c79207061696420666f722069742c206966207265717569726564206279206c6177> 55.17578125 <2e20506c6561736520736565>] TJ +ET + + +BT +220.912 40.088 Td +/F1.0 8 Tf +<687474703a2f2f627579776974686d652e636f6d2f70616765732f66617120666f72206d6f72652064657461696c73> Tj +ET + +Q + +endstream +endobj +18 0 obj +<>stream +0.000 0.000 0.000 rg +0.000 0.000 0.000 RG +q +1.000 0.000 0.000 RG + +q +561.600 0 0 254.991 25.200 501.009 cm +/I1 Do +Q + +BT +380.88 700.456 Td +/F2.0 13 Tf +<373238363438313231382d6d656d62> Tj +ET + +0.000 0.000 0.000 rg + +BT +43.2 681.008 Td +/F2.0 14 Tf +<4d6574726f526f636b> Tj +ET + +0.000 0.000 0.000 rg + +BT +43.2 661.502 Td +/F2.0 10 Tf +<5768617420796f7520676574> Tj +ET + + +BT +43.2 650.632 Td +/F1.0 10 Tf +<31206d6f6e7468206d656d626572736869702c20756e6c696d697465642072656e74616c7320616e6420626567696e6e65727320636c696d62696e6720636c617373> Tj +ET + + +BT +43.2 632.562 Td +/F2.0 10 Tf +<52656465656d61626c652061742074686520666f6c6c6f77696e67206c6f636174696f6e287329> Tj +ET + + +BT +43.2 621.692 Td +/F1.0 10 Tf +<3639204e6f726d616e2053747265657420457665726574742c204d41> Tj +ET + + +BT +43.2 596.422 Td +/F1.0 10 Tf +<20> Tj +ET + + +BT +381.6 665.92 Td +/F2.0 10 Tf +[<5075726368617365642062793a2053616d2054> 74.21875 <6f62696e2d686f63687374616474>] TJ +ET + +0.000 0.000 0.000 rg + +BT +381.6 575.92 Td +/F2.0 10 Tf +<497373756564206279204d6574726f526f636b> Tj +ET + + +BT +381.6 565.05 Td +/F2.0 10 Tf +[<497373756564206f6e20546875205365702032322031393a33303a33302055544320323031> 55.17578125 <31>] TJ +ET + + +BT +381.6 554.18 Td +/F2.0 10 Tf +<4e6f742076616c696420756e74696c3a20467269205365702032332031393a33303a333020555443> Tj +ET + + +BT +381.6 543.31 Td +/F2.0 10 Tf +[<323031> 55.17578125 <31>] TJ +ET + + +BT +381.6 532.44 Td +/F2.0 10 Tf +<50726f6d6f74696f6e616c2076616c756520657870697265733a20546875204d6172203232> Tj +ET + + +BT +381.6 521.57 Td +/F2.0 10 Tf +<30303a30303a3030202d3034303020323031322a> Tj +ET + +0.000 0.000 0.000 rg + +BT +36.0 488.064 Td +/F2.0 12 Tf +<4d6574726f526f636b20284d656d6229> Tj +ET + + +BT +36.0 467.92 Td +/F2.0 10 Tf +<496e737472756374696f6e73> Tj +ET + + +BT +36.0 449.85 Td +/F1.0 10 Tf +<2a20506c656173652063616c6c203631372d3338372d3736323520284576657265747429206f72203937382d3439392d37363235> Tj +ET + + +BT +36.0 438.98 Td +/F1.0 10 Tf +<284e657762757279706f72742920746f207265736572766520636c6173736573206f72206368616c6c656e676520636f757273652074696d652e> Tj +ET + + +BT +36.0 428.11 Td +/F1.0 10 Tf +[<466f722073687574746c6520736572766963652066726f6d207468652057> 18.06640625 <656c6c696e67746f6e20> 18.06640625 <54> 18.06640625 <2073746f702c20706c656173652063616c6c>] TJ +ET + + +BT +36.0 417.24 Td +/F1.0 10 Tf +<3631372d3338372d373632352075706f6e206172726976616c206174207468652073746174696f6e2e> Tj +ET + + +BT +36.0 406.37 Td +/F1.0 10 Tf +<2a205072696e7420766f756368657220616e64206272696e6720746f2061206d65726368616e74206c6f636174696f6e206c6973746564206f6e> Tj +ET + + +BT +36.0 395.5 Td +/F1.0 10 Tf +[<7468697320766f7563686572> 55.17578125 <2e>] TJ +ET + + +BT +36.0 384.63 Td +/F1.0 10 Tf +[<2a2057> 18.06640625 <7269746520796f7572206e616d6520616e642066756c6c206164647265737320666f756e64206f6e20796f757220494420696e20746865>] TJ +ET + + +BT +36.0 373.760000000001 Td +/F1.0 10 Tf +<73706163652070726f76696465642062656c6f77202874686520706572736f6e2072656465656d696e67207468697320766f7563686572> Tj +ET + + +BT +36.0 362.890000000001 Td +/F1.0 10 Tf +<73686f756c642070726f76696465207468697320696e666f726d6174696f6e2c206e6f74206e65636573736172696c7920746865> Tj +ET + + +BT +36.0 352.020000000001 Td +/F1.0 10 Tf +<70757263686173657220696620676976656e20617320612067696674292e20> Tj +ET + + +BT +36.0 341.150000000001 Td +/F1.0 10 Tf +<2a2050726573656e742076616c69642c20676f7665726e6d656e742d6973737565642070686f746f204944207768656e> Tj +ET + + +BT +36.0 330.280000000001 Td +/F1.0 10 Tf +<72656465656d696e6720796f757220766f75636865727320286e616d65206f6e20494420646f6573206e6f74206861766520746f> Tj +ET + + +BT +36.0 319.410000000001 Td +/F1.0 10 Tf +<6d6174636820746865207075726368617365722773206e616d6520696620676976656e20617320612067696674292e> Tj +ET + + +BT +36.0 308.540000000001 Td +ET + + +BT +36.0 297.670000000001 Td +/F1.0 10 Tf +[<496e737472756374696f6e7320666f72206d65726368616e743a2042757957> -0.0 <6974684d652068617320616c726561647920636f6c6c6563746564>] TJ +ET + + +BT +36.0 286.800000000001 Td +/F1.0 10 Tf +<7061796d656e74206f6e20796f757220626568616c662e> Tj +ET + + +BT +306.0 467.92 Td +/F2.0 10 Tf +<44657461696c73> Tj +ET + + +BT +306.0 449.85 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 438.98 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 428.11 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 417.24 Td +/F1.0 10 Tf +<6d656d6265727368697020626567696e732066726f6d20796f75722066697273742076697369743b20706c656173652070726573656e74> Tj +ET + + +BT +306.0 406.37 Td +/F1.0 10 Tf +<766f756368657220746f2061637469766174652e20> Tj +ET + + +BT +306.0 395.5 Td +/F1.0 10 Tf +[ -37.109375 18.06640625 <7320636c6173732e20> 55.17578125 <41> 55.17578125 <20636c617373206973206e6f74>] TJ +ET + + +BT +306.0 384.63 Td +/F1.0 10 Tf +[<6e656365737361727920696620796f7520616c7265616479206b6e6f7720686f7720746f2062656c6179> 74.21875 <2e>] TJ +ET + + +BT +306.0 373.760000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 362.890000000001 Td +/F1.0 10 Tf +<70756e636820636172642e> Tj +ET + + +BT +306.0 352.020000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 341.150000000001 Td +/F1.0 10 Tf +<6c6f636174696f6e206f6e6c793b206d757374206265206174206c6561737420342720362220746f20676f207468726f7567682074686520636f757273652e> Tj +ET + + +BT +306.0 330.280000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 319.410000000001 Td +/F1.0 10 Tf +[<617661696c6162696c697479> 74.21875 <2e>] TJ +ET + + +BT +306.0 308.540000000001 Td +/F1.0 10 Tf +[ 55.17578125 <4167657320313820616e6420756e646572206d7573742068617665206120776169766572207369676e6564206279206120706172656e74>] TJ +ET + + +BT +306.0 297.670000000001 Td +/F1.0 10 Tf +<6f72206c6567616c20677561726469616e2e> Tj +ET + + +BT +306.0 286.800000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 275.930000000001 Td +/F1.0 10 Tf +[<57> 18.06640625 <656c6c696e67746f6e20> 18.06640625 <54> 18.06640625 <2073746f702e>] TJ +ET + + +BT +306.0 265.060000000001 Td +/F1.0 10 Tf +[ 18.06640625 <66657273206f722070726f6d6f74696f6e732e>] TJ +ET + + +BT +306.0 254.190000000001 Td +/F1.0 10 Tf +[ 55.17578125 <6f7563686572206e6f742076616c696420756e74696c20746865206461792061667465722070757263686173652e>] TJ +ET + + +BT +306.0 243.320000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 232.450000000001 Td +/F1.0 10 Tf +<6d757374206265206163746976617465642062792030332f32322f323031322e> Tj +ET + + +BT +306.0 221.580000000001 Td +/F1.0 10 Tf + Tj +ET + + +BT +306.0 210.710000000001 Td +/F1.0 10 Tf +<30332f32322f323031322e> Tj +ET + + +BT +36.0 138.176 Td +/F2.0 8 Tf +[<4265666f72652072656465656d696e67207468697320766f7563686572> 55.17578125 <2c20706c6561736520777269746520696e207468652072657175657374656420696e666f726d6174696f6e2062656c6f77> 37.109375 <2e2020> 37.109375 <4174207468652074696d65206f6620726564656d7074696f6e2c20796f75206d7573742070726573656e7420612076616c69642c>] TJ +ET + + +BT +36.0 129.48 Td +/F2.0 8 Tf +<676f7665726e6d656e742d6973737565642070686f746f2049442074686174206d617463686573207468697320696e666f726d6174696f6e2e> Tj +ET + +0.749 0.749 0.749 RG +36.000 86.400 216.000 36.000 re +S + +BT +36.0 76.976 Td +/F1.0 8 Tf +<506c6561736520777269746520796f7572206e616d652068657265> Tj +ET + +252.000 86.400 216.000 36.000 re +S + +BT +252.0 76.976 Td +/F1.0 8 Tf +[<46756c6c20> 55.17578125 <41646472657373206173206c6973746564206f6e20796f7572204944>] TJ +ET + +468.000 86.400 108.000 36.000 re +S + +BT +468.0 76.976 Td +/F1.0 8 Tf +<5374617465206c6973746564206f6e20796f7572204944> Tj +ET + + +BT +239.576 66.176 Td +/F1.0 8 Tf +[<5468616e6b20796f7520666f72207573696e672042757957> -0.0 <6974684d652e636f6d21>] TJ +ET + + +BT +151.5 57.48 Td +/F1.0 8 Tf +<496620796f75206e65656420616e7920617373697374616e63652c20796f752063616e20726561636820757320617420696e666f40627579776974686d652e636f6d206f72203836362d3638302d37303038> Tj +ET + + +BT +67.292703125 48.784 Td +/F1.0 8 Tf +[<2a416674657220746869732065787069726174696f6e20646174652c20796f75206d6179207374696c6c2062652061626c6520746f2072656465656d207468697320766f756368657220666f7220746865207072696365206f726967696e616c6c79207061696420666f722069742c206966207265717569726564206279206c6177> 55.17578125 <2e20506c6561736520736565>] TJ +ET + + +BT +220.912 40.088 Td +/F1.0 8 Tf +<687474703a2f2f627579776974686d652e636f6d2f70616765732f66617120666f72206d6f72652064657461696c73> Tj +ET + +Q + +BT +36.0 138.176 Td +/F2.0 8 Tf +[<4265666f72652072656465656d696e67207468697320766f7563686572> 55.17578125 <2c20706c6561736520777269746520696e207468652072657175657374656420696e666f726d6174696f6e2062656c6f77> 37.109375 <2e2020> 37.109375 <4174207468652074696d65206f6620726564656d7074696f6e2c20796f75206d7573742070726573656e7420612076616c69642c>] TJ +ET + + +BT +36.0 129.48 Td +/F2.0 8 Tf +<676f7665726e6d656e742d6973737565642070686f746f2049442074686174206d617463686573207468697320696e666f726d6174696f6e2e> Tj +ET + +0.749 0.749 0.749 RG +36.000 86.400 216.000 36.000 re +S + +BT +36.0 76.976 Td +/F1.0 8 Tf +<506c6561736520777269746520796f7572206e616d652068657265> Tj +ET + +252.000 86.400 216.000 36.000 re +S + +BT +252.0 76.976 Td +/F1.0 8 Tf +[<46756c6c20> 55.17578125 <41646472657373206173206c6973746564206f6e20796f7572204944>] TJ +ET + +468.000 86.400 108.000 36.000 re +S + +BT +468.0 76.976 Td +/F1.0 8 Tf +<5374617465206c6973746564206f6e20796f7572204944> Tj +ET + + +BT +239.576 66.176 Td +/F1.0 8 Tf +[<5468616e6b20796f7520666f72207573696e672042757957> -0.0 <6974684d652e636f6d21>] TJ +ET + + +BT +151.5 57.48 Td +/F1.0 8 Tf +<496620796f75206e65656420616e7920617373697374616e63652c20796f752063616e20726561636820757320617420696e666f40627579776974686d652e636f6d206f72203836362d3638302d37303038> Tj +ET + + +BT +67.292703125 48.784 Td +/F1.0 8 Tf +[<2a416674657220746869732065787069726174696f6e20646174652c20796f75206d6179207374696c6c2062652061626c6520746f2072656465656d207468697320766f756368657220666f7220746865207072696365206f726967696e616c6c79207061696420666f722069742c206966207265717569726564206279206c6177> 55.17578125 <2e20506c6561736520736565>] TJ +ET + + +BT +220.912 40.088 Td +/F1.0 8 Tf +<687474703a2f2f627579776974686d652e636f6d2f70616765732f66617120666f72206d6f72652064657461696c73> Tj +ET + +Q + +endstream +endobj +20 0 obj +<> +endobj +xref +0 21 +0000000000 65535 f +0000000015 00000 n +0000000196 00000 n +0000095543 00000 n +0000000230 00000 n +0000011046 00000 n +0000048495 00000 n +0000070626 00000 n +0000048758 00000 n +0000049039 00000 n +0000049952 00000 n +0000050266 00000 n +0000070850 00000 n +0000071765 00000 n +0000072039 00000 n +0000095620 00000 n +0000095807 00000 n +0000096044 00000 n +0000106861 00000 n +0000095998 00000 n +0000117678 00000 n +trailer +<<65e6ebcb3171fabdc484bf86c3dc1c63>]/Info 20 0 R/Size 21>> +startxref +117824 +%%EOF diff --git a/test/test_manifest.json b/test/test_manifest.json index 16d924151..a1f32a5cd 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -472,6 +472,12 @@ "rounds": 1, "type": "eq" }, + { "id": "issue1350", + "file": "pdfs/issue1350.pdf", + "md5": "92f72a04a4d9d05b2dd433b51f32ab1f", + "rounds": 1, + "type": "eq" + }, { "id": "issue925", "file": "pdfs/issue925.pdf", "md5": "f58fe943090aff89dcc8e771bc0db4c2", From 6f5baaa60b665e06dd4ccced3e0d0db9b790d30b Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 19 Mar 2012 10:41:13 -0700 Subject: [PATCH 13/54] Fix names, remove debug function. --- src/fonts.js | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index 6e130ae7e..3dd0ff571 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -3948,7 +3948,7 @@ var CFF = (function CFFClosure() { return CFF; })(); -var CFFHeader = (function CFFHeader() { +var CFFHeader = (function CFFHeaderClosure() { function CFFHeader(major, minor, hdrSize, offSize) { this.major = major; this.minor = minor; @@ -3958,7 +3958,7 @@ var CFFHeader = (function CFFHeader() { return CFFHeader; })(); -var CFFStrings = (function CFFStrings() { +var CFFStrings = (function CFFStringsClosure() { function CFFStrings() { this.strings = []; } @@ -3980,7 +3980,7 @@ var CFFStrings = (function CFFStrings() { return CFFStrings; })(); -var CFFIndex = (function() { +var CFFIndex = (function CFFIndexClosure() { function CFFIndex() { this.objects = []; this.length = 0; @@ -4039,16 +4039,6 @@ var CFFDict = (function CFFDictClosure() { }, removeByName: function removeByName(name) { delete this.values[this.nameToKeyMap[name]]; - }, - dump: function dump(title) { - console.log('----' + title + ' Dictionary Dump------'); - for (var key in this.values) { - if (key in this.keyToNameMap) - console.log(this.keyToNameMap[key] + '(' + key + '): ' + - this.values[key]); - else - console.log('Unknown(' + key + '): ' + this.values[key]); - } } }; CFFDict.createTables = function createTables(layout) { From d2f463bf7c1a912c9616a3c90c135d622c5da626 Mon Sep 17 00:00:00 2001 From: gigaherz Date: Wed, 21 Mar 2012 23:36:10 +0100 Subject: [PATCH 14/54] Add a progressbar to the loading indicator, below the text. --- web/viewer.css | 20 +++++++++++++++++- web/viewer.html | 5 ++++- web/viewer.js | 54 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/web/viewer.css b/web/viewer.css index fdce0288a..536073c34 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -391,11 +391,29 @@ canvas { } } -#loading { +#loadingBox { margin: 100px 0; text-align: center; } +#loadingBar { + display: inline-block; + border: 1px solid black; + clear: both; + padding: 1px; + margin:0px; +} + +#loadingBar #progress { + background-color: green; + display: inline-block; +} + +#loadingBar #remaining { + background-color: #333; + display: inline-block; +} + #PDFBug { font-size: 10px; position: fixed; diff --git a/web/viewer.html b/web/viewer.html index 34b2e77cb..b30b52db9 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -142,7 +142,10 @@ -
Loading... 0%
+
+
Loading... 0%
+
+
diff --git a/web/viewer.js b/web/viewer.js index 67ef67e97..b8d7b44f0 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -27,6 +27,49 @@ var Cache = function cacheCache(size) { }; }; +var ProgressBar = (function ProgressBarClosure() { + + function clamp(v, min, max) { + return Math.min(Math.max(v, min), max); + } + + function sizeBar(bar, num, width, height, units) { + var progress = bar.querySelector('#progress'); + var remaining = bar.querySelector('#remaining'); + progress.style.height=height + units; + remaining.style.height=height + units; + progress.style.width=num + units; + remaining.style.width=(width - num) + units; + } + + function ProgressBar(element, min, max, width, height, units) { + this.element = element; + this.min = min; + this.max = max; + this.width = width; + this.height = height; + this.units = units; + this.value = min; + sizeBar(element, 0, this.width, this.height, this.units); + } + + ProgressBar.prototype = { + constructor: ProgressBar, + + get value() { + return this._value; + }, + + set value(val) { + this._value = clamp(val, this.min, this.max); + var num = this.width * (val - this.min) / (this.max - this.min); + sizeBar(this.element, num, this.width, this.height, this.units); + } + }; + + return ProgressBar; +})(); + var RenderingQueue = (function RenderingQueueClosure() { function RenderingQueue() { this.items = []; @@ -260,6 +303,11 @@ var PDFView = { open: function pdfViewOpen(url, scale) { document.title = this.url = url; + // FIXME: Probably needs a better place to get initialized + if(!PDFView.loadingProgress) { + PDFView.loadingProgress = new ProgressBar(document.getElementById('loadingBar'), 0, 100, 15, 1.5, 'em'); + } + var self = this; PDFJS.getPdf( { @@ -400,6 +448,8 @@ var PDFView = { var percent = Math.round(level * 100); var loadingIndicator = document.getElementById('loading'); loadingIndicator.textContent = 'Loading... ' + percent + '%'; + + PDFView.loadingProgress.value = percent; }, load: function pdfViewLoad(data, scale) { @@ -414,8 +464,8 @@ var PDFView = { var errorWrapper = document.getElementById('errorWrapper'); errorWrapper.setAttribute('hidden', 'true'); - var loadingIndicator = document.getElementById('loading'); - loadingIndicator.setAttribute('hidden', 'true'); + var loadingBox = document.getElementById('loadingBox'); + loadingBox.setAttribute('hidden', 'true'); var sidebar = document.getElementById('sidebarView'); sidebar.parentNode.scrollTop = 0; From ce8b0625b18302e1a29a0d7e256e18d6f80d99db Mon Sep 17 00:00:00 2001 From: gigaherz Date: Wed, 21 Mar 2012 23:56:20 +0100 Subject: [PATCH 15/54] Cosmetic changes --- web/viewer.css | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/web/viewer.css b/web/viewer.css index 536073c34..eac80da84 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -401,12 +401,17 @@ canvas { border: 1px solid black; clear: both; padding: 1px; - margin:0px; + line-height: 0; } #loadingBar #progress { - background-color: green; display: inline-block; + background: -moz-linear-gradient(top, #b4e391 0%, #61c419 50%, #b4e391 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b4e391), color-stop(50%,#61c419), color-stop(100%,#b4e391)); + background: -webkit-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%); + background: -o-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%); + background: -ms-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%); + background: linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%); } #loadingBar #remaining { From 37fef69e5e14e2ab8ef862887533ee09fbbfd3d7 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 21 Mar 2012 16:03:17 -0700 Subject: [PATCH 16/54] Remove slice. --- src/fonts.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index 3dd0ff571..28a95acbb 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -3773,13 +3773,13 @@ var CFFParser = (function CFFParserClosure() { parseCharsets: function parsecharsets(pos, length, strings, cid) { if (pos == 0) { return new CFFCharset(true, CFFCharsetPredefinedTypes.ISO_ADOBE, - ISOAdobeCharset.slice()); + ISOAdobeCharset); } else if (pos == 1) { return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT, - ExpertCharset.slice()); + ExpertCharset); } else if (pos == 2) { return new CFFCharset(true, CFFCharsetPredefinedTypes.EXPERT_SUBSET, - ExpertSubsetCharset.slice()); + ExpertSubsetCharset); } var bytes = this.bytes; From ecaf467a1396c9e91732eba5f16ff36c702607ae Mon Sep 17 00:00:00 2001 From: gigaherz Date: Thu, 22 Mar 2012 00:05:24 +0100 Subject: [PATCH 17/54] One more cosmetic tweak and fix 'make lint' complaints. --- web/viewer.css | 1 - web/viewer.js | 16 +++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/web/viewer.css b/web/viewer.css index eac80da84..b316ddcb5 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -400,7 +400,6 @@ canvas { display: inline-block; border: 1px solid black; clear: both; - padding: 1px; line-height: 0; } diff --git a/web/viewer.js b/web/viewer.js index b8d7b44f0..246577a52 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -32,14 +32,14 @@ var ProgressBar = (function ProgressBarClosure() { function clamp(v, min, max) { return Math.min(Math.max(v, min), max); } - + function sizeBar(bar, num, width, height, units) { var progress = bar.querySelector('#progress'); var remaining = bar.querySelector('#remaining'); - progress.style.height=height + units; - remaining.style.height=height + units; - progress.style.width=num + units; - remaining.style.width=(width - num) + units; + progress.style.height = height + units; + remaining.style.height = height + units; + progress.style.width = num + units; + remaining.style.width = (width - num) + units; } function ProgressBar(element, min, max, width, height, units) { @@ -304,8 +304,10 @@ var PDFView = { document.title = this.url = url; // FIXME: Probably needs a better place to get initialized - if(!PDFView.loadingProgress) { - PDFView.loadingProgress = new ProgressBar(document.getElementById('loadingBar'), 0, 100, 15, 1.5, 'em'); + if (!PDFView.loadingProgress) { + PDFView.loadingProgress = new ProgressBar( + document.getElementById('loadingBar'), + 0, 100, 15, 1.5, 'em'); } var self = this; From 8e060bdbca7fab0d7f65d4a247b63d24a825fd58 Mon Sep 17 00:00:00 2001 From: Kalervo Kujala Date: Thu, 22 Mar 2012 15:15:27 +0200 Subject: [PATCH 18/54] Use [] instead of new Array(...). --- src/bidi.js | 12 ++++-------- src/colorspace.js | 2 +- src/fonts.js | 4 ++-- src/function.js | 4 ++-- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/bidi.js b/src/bidi.js index aab477dbc..5f18e5303 100644 --- a/src/bidi.js +++ b/src/bidi.js @@ -132,9 +132,9 @@ var bidi = PDFJS.bidi = (function bidiClosure() { // get types, fill arrays - var chars = new Array(strLength); - var types = new Array(strLength); - var oldtypes = new Array(strLength); + var chars = []; + var types = []; + var oldtypes = []; var numBidi = 0; for (var i = 0; i < strLength; ++i) { @@ -176,16 +176,12 @@ var bidi = PDFJS.bidi = (function bidiClosure() { } } - var levels = new Array(strLength); + var levels = []; for (var i = 0; i < strLength; ++i) { levels[i] = startLevel; } - var diffChars = new Array(strLength); - var diffLevels = new Array(strLength); - var diffTypes = new Array(strLength); - /* X1-X10: skip most of this, since we are NOT doing the embeddings. */ diff --git a/src/colorspace.js b/src/colorspace.js index d3d392361..e1df7c725 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -220,7 +220,7 @@ var AlternateCS = (function AlternateCSClosure() { var baseNumComps = base.numComps; var baseBuf = new Uint8Array(baseNumComps * length); var numComps = this.numComps; - var scaled = new Array(numComps); + var scaled = []; for (var i = 0; i < length; i += numComps) { for (var z = 0; z < numComps; ++z) diff --git a/src/fonts.js b/src/fonts.js index 8473196a0..43efea34d 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -3652,7 +3652,7 @@ var CFFParser = (function CFFParserClosure() { var name = index.get(i); // OTS doesn't allow names to be over 127 characters. var length = Math.min(name.length, 127); - var data = new Array(length); + var data = []; // OTS also only permits certain characters in the name. for (var j = 0; j < length; ++j) { var c = name[j]; @@ -4502,7 +4502,7 @@ var CFFCompiler = (function CFFCompilerClosure() { return this.compileTypedArray(fdSelect); }, compileTypedArray: function compileTypedArray(data) { - var out = new Array(data.length); + var out = []; for (var i = 0, ii = data.length; i < ii; ++i) out[i] = data[i]; return out; diff --git a/src/function.js b/src/function.js index 4f81158f0..5ff5840c5 100644 --- a/src/function.js +++ b/src/function.js @@ -81,7 +81,7 @@ var PDFFunction = (function PDFFunctionClosure() { function toMultiArray(arr) { var inputLength = arr.length; var outputLength = arr.length / 2; - var out = new Array(outputLength); + var out = []; var index = 0; for (var i = 0; i < inputLength; i += 2) { out[index] = [arr[i], arr[i + 1]]; @@ -364,7 +364,7 @@ var PDFFunction = (function PDFFunctionClosure() { return cache.get(key); var stack = evaluator.execute(initialStack); - var transformed = new Array(numOutputs); + var transformed = []; for (i = numOutputs - 1; i >= 0; --i) { var out = stack.pop(); var rangeIndex = 2 * i; From 114d2c9ebd806f1aaab2029211bcf159cef5266a Mon Sep 17 00:00:00 2001 From: gigaherz Date: Thu, 22 Mar 2012 22:51:10 +0100 Subject: [PATCH 19/54] Simplified ProgressBar class. Visual tweaks. --- web/viewer.css | 16 +++++++-- web/viewer.html | 2 +- web/viewer.js | 87 +++++++++++++++++++++++++------------------------ 3 files changed, 59 insertions(+), 46 deletions(-) diff --git a/web/viewer.css b/web/viewer.css index b316ddcb5..e8e986804 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -400,22 +400,32 @@ canvas { display: inline-block; border: 1px solid black; clear: both; + margin:0px; line-height: 0; + border-radius: 4px; } -#loadingBar #progress { +#loadingBar .progress { + background-color: green; display: inline-block; + + background: #b4e391; background: -moz-linear-gradient(top, #b4e391 0%, #61c419 50%, #b4e391 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b4e391), color-stop(50%,#61c419), color-stop(100%,#b4e391)); background: -webkit-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%); background: -o-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%); background: -ms-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%); - background: linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%); + background: linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%); + + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; } -#loadingBar #remaining { +#loadingBar .remaining { background-color: #333; display: inline-block; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; } #PDFBug { diff --git a/web/viewer.html b/web/viewer.html index b30b52db9..6528d484f 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -144,7 +144,7 @@
Loading... 0%
-
+
diff --git a/web/viewer.js b/web/viewer.js index 246577a52..ad24bc0a7 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -29,45 +29,49 @@ var Cache = function cacheCache(size) { var ProgressBar = (function ProgressBarClosure() { - function clamp(v, min, max) { - return Math.min(Math.max(v, min), max); + function clamp(v, min, max) { + return Math.min(Math.max(v, min), max); + } + + function ProgressBar(id, opts) { + + // Fetch the sub-elements for later + this.progressDiv = document.querySelector(id + ' .progress'); + this.remainingDiv = document.querySelector(id + ' .remaining'); + + // Get options, with sensible defaults + this.height = opts.height || 1; + this.width = opts.width || 15; + this.units = opts.units || 'em'; + this.percent = opts.progress || 0; + + // Initialize heights + this.progressDiv.style.height = this.height + this.units; + this.remainingDiv.style.height = this.height + this.units; + } + + ProgressBar.prototype = { + constructor: ProgressBar, + + updateBar: function ProgressBar_updateBar() { + var progressSize = this.width * this._percent / 100; + var remainingSize = (this.width - progressSize); + + this.progressDiv.style.width = progressSize + this.units; + this.remainingDiv.style.width = remainingSize + this.units; + }, + + get percent() { + return this._percent; + }, + + set percent(val) { + this._percent = clamp(val, 0, 100); + this.updateBar(); } + }; - function sizeBar(bar, num, width, height, units) { - var progress = bar.querySelector('#progress'); - var remaining = bar.querySelector('#remaining'); - progress.style.height = height + units; - remaining.style.height = height + units; - progress.style.width = num + units; - remaining.style.width = (width - num) + units; - } - - function ProgressBar(element, min, max, width, height, units) { - this.element = element; - this.min = min; - this.max = max; - this.width = width; - this.height = height; - this.units = units; - this.value = min; - sizeBar(element, 0, this.width, this.height, this.units); - } - - ProgressBar.prototype = { - constructor: ProgressBar, - - get value() { - return this._value; - }, - - set value(val) { - this._value = clamp(val, this.min, this.max); - var num = this.width * (val - this.min) / (this.max - this.min); - sizeBar(this.element, num, this.width, this.height, this.units); - } - }; - - return ProgressBar; + return ProgressBar; })(); var RenderingQueue = (function RenderingQueueClosure() { @@ -304,10 +308,9 @@ var PDFView = { document.title = this.url = url; // FIXME: Probably needs a better place to get initialized - if (!PDFView.loadingProgress) { - PDFView.loadingProgress = new ProgressBar( - document.getElementById('loadingBar'), - 0, 100, 15, 1.5, 'em'); + if (!PDFView.loadingBar) { + PDFView.loadingBar = new ProgressBar('#loadingBar', { + width: 15, height: 1.5, units: 'em'}); } var self = this; @@ -451,7 +454,7 @@ var PDFView = { var loadingIndicator = document.getElementById('loading'); loadingIndicator.textContent = 'Loading... ' + percent + '%'; - PDFView.loadingProgress.value = percent; + PDFView.loadingBar.percent = percent; }, load: function pdfViewLoad(data, scale) { From ddf3d114d09a921af3ed626301e2da50574bbc6d Mon Sep 17 00:00:00 2001 From: gigaherz Date: Thu, 22 Mar 2012 22:57:42 +0100 Subject: [PATCH 20/54] Wrong word. --- web/viewer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/viewer.js b/web/viewer.js index ad24bc0a7..3f6fef957 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -43,7 +43,7 @@ var ProgressBar = (function ProgressBarClosure() { this.height = opts.height || 1; this.width = opts.width || 15; this.units = opts.units || 'em'; - this.percent = opts.progress || 0; + this.percent = opts.percent || 0; // Initialize heights this.progressDiv.style.height = this.height + this.units; From a30f54078d41afda1744cb1f70334c5b19f7c96b Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Thu, 22 Mar 2012 18:07:40 -0500 Subject: [PATCH 21/54] fixing usmanm-bad.pdf link --- test/pdfs/usmanm-bad.pdf.link | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pdfs/usmanm-bad.pdf.link b/test/pdfs/usmanm-bad.pdf.link index f7ebc3610..e37e5aa2b 100644 --- a/test/pdfs/usmanm-bad.pdf.link +++ b/test/pdfs/usmanm-bad.pdf.link @@ -1 +1 @@ -http://www.mit.edu/~6.033/writing-samples/usmanm_dp1.pdf +http://web.mit.edu/6.033/2011/wwwdocs/writing-samples/usmanm_dp1.pdf From 22c858c2eca56d9be135ea187226c0d1e16f5309 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Fri, 23 Mar 2012 14:28:09 -0700 Subject: [PATCH 22/54] Use a different way to add the event listener. --- .../firefox/components/PdfStreamConverter.js | 75 ++++++++----------- 1 file changed, 30 insertions(+), 45 deletions(-) diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index 9375a2690..da7748f32 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -30,23 +30,11 @@ function log(aMsg) { Services.console.logStringMessage(msg); dump(msg + '\n'); } -function getWindow(top, id) { - return top.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .getOuterWindowWithId(id); -} -function windowID(win) { - return win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .outerWindowID; -} -function topWindow(win) { - return win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); + +function getDOMWindow(aChannel) { + var requestor = aChannel.notificationCallbacks; + var win = requestor.getInterface(Components.interfaces.nsIDOMWindow); + return win; } // All the priviledged actions. @@ -75,6 +63,12 @@ ChromeActions.prototype = { } }; +function getDOMWindow(aChannel) { + var requestor = aChannel.notificationCallbacks; + var win = requestor.getInterface(Components.interfaces.nsIDOMWindow); + return win; +} + // Event listener to trigger chrome privedged code. function RequestListener(actions) { this.actions = actions; @@ -163,38 +157,29 @@ PdfStreamConverter.prototype = { var channel = ioService.newChannel( 'resource://pdf.js/web/viewer.html', null, null); - // Keep the URL the same so the browser sees it as the same. - channel.originalURI = aRequest.URI; - channel.asyncOpen(this.listener, aContext); - - // Setup a global listener waiting for the next DOM to be created and verfiy - // that its the one we want by its URL. When the correct DOM is found create - // an event listener on that window for the pdf.js events that require - // chrome priviledges. Code snippet from John Galt. - let window = aRequest.loadGroup.groupObserver - .QueryInterface(Ci.nsIWebProgress) - .DOMWindow; - let top = topWindow(window); - let id = windowID(window); - window = null; - - top.addEventListener('DOMWindowCreated', function onDOMWinCreated(event) { - let doc = event.originalTarget; - let win = doc.defaultView; - - if (id == windowID(win)) { - top.removeEventListener('DOMWindowCreated', onDOMWinCreated, true); - if (!doc.documentURIObject.equals(aRequest.URI)) - return; - + var listener = this.listener; + // Proxy all the requst observer calls, when it gets to onStopRequst + // we can get the dom window. + var proxy = { + onStartRequest: function() { + listener.onStartRequest.apply(listener, arguments); + }, + onDataAvailable: function() { + listener.onDataAvailable.apply(listener, arguments); + }, + onStopRequest: function() { + var domWindow = getDOMWindow(channel); let requestListener = new RequestListener(new ChromeActions); - win.addEventListener(PDFJS_EVENT_ID, function(event) { + domWindow.addEventListener(PDFJS_EVENT_ID, function(event) { requestListener.receive(event); }, false, true); - } else if (!getWindow(top, id)) { - top.removeEventListener('DOMWindowCreated', onDOMWinCreated, true); + listener.onStopRequest.apply(listener, arguments); } - }, true); + }; + + // Keep the URL the same so the browser sees it as the same. + channel.originalURI = aRequest.URI; + channel.asyncOpen(proxy, aContext); }, // nsIRequestObserver::onStopRequest From 11e84b2b7b3ae89fb101bbef5d3ab8d086d5b8ce Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Fri, 23 Mar 2012 14:32:19 -0700 Subject: [PATCH 23/54] Remove duplicaten dom window func. --- extensions/firefox/components/PdfStreamConverter.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index da7748f32..64cd13a38 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -63,11 +63,6 @@ ChromeActions.prototype = { } }; -function getDOMWindow(aChannel) { - var requestor = aChannel.notificationCallbacks; - var win = requestor.getInterface(Components.interfaces.nsIDOMWindow); - return win; -} // Event listener to trigger chrome privedged code. function RequestListener(actions) { From a79431984530554402af918f1582d325ac7f313b Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Fri, 23 Mar 2012 14:48:50 -0700 Subject: [PATCH 24/54] Add url check. --- extensions/firefox/components/PdfStreamConverter.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index 64cd13a38..4467abc6b 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -164,10 +164,13 @@ PdfStreamConverter.prototype = { }, onStopRequest: function() { var domWindow = getDOMWindow(channel); - let requestListener = new RequestListener(new ChromeActions); - domWindow.addEventListener(PDFJS_EVENT_ID, function(event) { - requestListener.receive(event); - }, false, true); + // Double check the url is still the correct one. + if (domWindow.document.documentURIObject.equals(aRequest.URI)) { + let requestListener = new RequestListener(new ChromeActions); + domWindow.addEventListener(PDFJS_EVENT_ID, function(event) { + requestListener.receive(event); + }, false, true); + } listener.onStopRequest.apply(listener, arguments); } }; From be8daec13f1faa2c3d7ad44e43f7bf0346e1ca8a Mon Sep 17 00:00:00 2001 From: gigaherz Date: Sat, 24 Mar 2012 16:22:55 +0100 Subject: [PATCH 25/54] Adding myself to the contributor list. --- LICENSE | 1 + 1 file changed, 1 insertion(+) diff --git a/LICENSE b/LICENSE index a3e99545a..f01ded412 100644 --- a/LICENSE +++ b/LICENSE @@ -12,6 +12,7 @@ Jakob Miland Artur Adib Brendan Dahl + David Quintana Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), From 20dd225e40ecbf4e28257df00581a7ce45f467d9 Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Sat, 24 Mar 2012 19:59:51 +0100 Subject: [PATCH 26/54] Metadata parsing/serialization --- Makefile | 1 + make.js | 3 +- src/metadata.js | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ src/obj.js | 21 +++++++++++++ web/viewer.html | 1 + web/viewer.js | 8 +++++ 6 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/metadata.js diff --git a/Makefile b/Makefile index 62565670a..3cc423350 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ PDF_JS_FILES = \ ../external/jpgjs/jpg.js \ jpx.js \ bidi.js \ + metadata.js \ $(NULL) # make server diff --git a/make.js b/make.js index 33771aeb7..69732667f 100755 --- a/make.js +++ b/make.js @@ -97,7 +97,8 @@ target.bundle = function() { 'worker.js', '../external/jpgjs/jpg.js', 'jpx.js', - 'bidi.js']; + 'bidi.js', + 'metadata-js']; if (!exists(BUILD_DIR)) mkdir(BUILD_DIR); diff --git a/src/metadata.js b/src/metadata.js new file mode 100644 index 000000000..68b19764b --- /dev/null +++ b/src/metadata.js @@ -0,0 +1,80 @@ +var Metadata = (function MetadataClosure() { + function Metadata(meta) { + if (typeof meta === 'string') { + var parser = new DOMParser(); + meta = parser.parseFromString(meta, 'application/xml'); + } else if (!(meta instanceof Document)) { + error('Metadata: Invalid metadata object'); + } + + this.metaDocument = meta; + this.metadata = {}; + this.parse(); + } + + Metadata.prototype = { + parse: function() { + var doc = this.metaDocument; + var rdf = doc.documentElement; + if (rdf.tagName.toLowerCase() !== 'rdf:rdf') { // Wrapped in + rdf = rdf.firstChild; + while (rdf.nodeName && rdf.nodeName.toLowerCase() !== 'rdf:rdf') { + rdf = rdf.nextSibling; + } + } + if (rdf.nodeName.toLowerCase() !== 'rdf:rdf' || !rdf.hasChildNodes()) { + return; + } + + var childNodes = rdf.childNodes, desc, namespace, entries, entry; + + for (var i = 0, length = childNodes.length; i < length; i++) { + desc = childNodes[i]; + if (desc.nodeName.toLowerCase() !== 'rdf:description') { + continue; + } + + entries = []; + for (var ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) { + if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') { + entries.push(desc.childNodes[ii]); + } + } + + for (ii = 0, iLength = entries.length; ii < iLength; ii++) { + var entry = entries[ii]; + var name = entry.nodeName.toLowerCase(); + var entryName = name.split(':'); + entryName = (entryName.length > 1) ? entryName[1] : entryName[0]; + switch (name) { + case 'pdf:moddate': + case 'xap:createdate': + case 'xap:metadatadate': + case 'xap:modifydate': + this.metadata[entryName] = new Date(entry.textContent.trim()); + break; + + default: + // For almost all entries we just add them to the metadata object + if (this.metadata[entryName]) { + this.metadata[name] = entry.textContent.trim(); + } else { + this.metadata[entryName] = entry.textContent.trim(); + } + break; + } + } + } + }, + + get: function(name) { + return this.metadata[name] || null; + }, + + has: function(name) { + return typeof this.metadata[name] !== 'undefined'; + } + }; + + return Metadata; +})(); diff --git a/src/obj.js b/src/obj.js index 3c649fb06..144a3a377 100644 --- a/src/obj.js +++ b/src/obj.js @@ -111,6 +111,27 @@ var Catalog = (function CatalogClosure() { } Catalog.prototype = { + get metadata() { + var ref = this.catDict.get('Metadata'); + if (!ref) { + return null; + } + + var stream = this.xref.fetch(ref); + var dict = stream.dict; + if (isDict(dict)) { + var type = dict.get('Type'); + var subtype = dict.get('Subtype'); + + if(isName(type) && isName(subtype) && + type.name === 'Metadata' && subtype.name === 'XML') { + var metadata = stringToPDFString(bytesToString(stream.getbytes())); + return metadata; + } + } + + return null; + }, get toplevelPagesDict() { var pagesObj = this.catDict.get('Pages'); assertWellFormed(isRef(pagesObj), 'invalid top-level pages reference'); diff --git a/web/viewer.html b/web/viewer.html index 34b2e77cb..2806d3a7e 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -11,6 +11,7 @@ + diff --git a/web/viewer.js b/web/viewer.js index 67ef67e97..68577ad34 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -499,6 +499,14 @@ var PDFView = { // Setting the default one. this.parseScale(kDefaultScale, true); } + + var metadata = pdf.catalog.metadata; + if (metadata) { + this.metadata = metadata = new Metadata(metadata); + if (metadata.has('title')) { + document.title = metadata.get('title'); + } + } }, setHash: function pdfViewSetHash(hash) { From 9dbcc74d41d8319701d62e42f42bcd35acbee863 Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Sat, 24 Mar 2012 20:02:20 +0100 Subject: [PATCH 27/54] lint.. --- src/obj.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obj.js b/src/obj.js index 144a3a377..d4187b4fa 100644 --- a/src/obj.js +++ b/src/obj.js @@ -123,7 +123,7 @@ var Catalog = (function CatalogClosure() { var type = dict.get('Type'); var subtype = dict.get('Subtype'); - if(isName(type) && isName(subtype) && + if (isName(type) && isName(subtype) && type.name === 'Metadata' && subtype.name === 'XML') { var metadata = stringToPDFString(bytesToString(stream.getbytes())); return metadata; From ab198e89cc17029de066cda769afffe6709d7ed5 Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Sat, 24 Mar 2012 23:39:03 +0100 Subject: [PATCH 28/54] Typo from git'ing around --- src/obj.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/obj.js b/src/obj.js index d4187b4fa..21f20c3fb 100644 --- a/src/obj.js +++ b/src/obj.js @@ -125,7 +125,7 @@ var Catalog = (function CatalogClosure() { if (isName(type) && isName(subtype) && type.name === 'Metadata' && subtype.name === 'XML') { - var metadata = stringToPDFString(bytesToString(stream.getbytes())); + var metadata = stringToPDFString(bytesToString(stream.getBytes())); return metadata; } } From efa89ba41a0b90a4e9a3ef2de711d80070fc192e Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Sun, 25 Mar 2012 13:00:43 +0200 Subject: [PATCH 29/54] Fix issue #1302 --- src/stream.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream.js b/src/stream.js index f76a07b4c..d31f3d50b 100644 --- a/src/stream.js +++ b/src/stream.js @@ -2056,7 +2056,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { if (this.eoblock) { code = this.lookBits(7); p = twoDimTable[code]; - if (p[0] > 0) { + if (p && p[0] > 0) { this.eatBits(p[0]); return p[1]; } From 72355121a0933b384718096ebe638cf0df786933 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 25 Mar 2012 14:15:40 -0500 Subject: [PATCH 30/54] Don't print missing symbols in the font --- src/canvas.js | 2 ++ src/fonts.js | 29 +++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/canvas.js b/src/canvas.js index 7bf94a642..2528a6b3b 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -751,6 +751,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { x += Util.sign(current.fontMatrix[0]) * wordSpacing; continue; } + if (glyph.disabled) + continue; var char = glyph.fontChar; var charWidth = glyph.width * fontSize * 0.001 + diff --git a/src/fonts.js b/src/fonts.js index 500ef8780..5d63d7c50 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1828,8 +1828,9 @@ var Font = (function FontClosure() { readGlyphNameMap(post, properties); } - // Replace the old CMAP table with a shiny new one + var glyphs, ids; if (properties.type == 'CIDFontType2') { + // Replace the old CMAP table with a shiny new one // Type2 composite fonts map characters directly to glyphs so the cmap // table must be replaced. // canvas fillText will reencode some characters even if the font has a @@ -1861,7 +1862,9 @@ var Font = (function FontClosure() { } } - var glyphs = [], ids = []; + glyphs = []; + ids = []; + var usedUnicodes = []; var unassignedUnicodeItems = []; for (var i = 1; i < numGlyphs; i++) { @@ -1892,11 +1895,12 @@ var Font = (function FontClosure() { glyphs.push({ unicode: unicode, code: cid }); ids.push(i); } - cmap.data = createCMapTable(glyphs, ids); } else { var cmapTable = readCMapTable(cmap, font); - var glyphs = cmapTable.glyphs; - var ids = cmapTable.ids; + + glyphs = cmapTable.glyphs; + ids = cmapTable.ids; + var hasShortCmap = !!cmapTable.hasShortCmap; var toFontChar = this.toFontChar; @@ -2049,10 +2053,16 @@ var Font = (function FontClosure() { createGlyphNameMap(glyphs, ids, properties); this.glyphNameMap = properties.glyphNameMap; - - cmap.data = createCMapTable(glyphs, ids); } + // Converting glyphs and ids into font's cmap table + cmap.data = createCMapTable(glyphs, ids); + var unicodeIsEnabled = []; + for (var i = 0, ii = glyphs.length; i < ii; i++) { + unicodeIsEnabled[glyphs[i].unicode] = true; + } + this.unicodeIsEnabled = unicodeIsEnabled; + // Rewrite the 'post' table if needed if (requiredTables.indexOf('post') != -1) { tables.push({ @@ -2378,7 +2388,7 @@ var Font = (function FontClosure() { }, charToGlyph: function fonts_charToGlyph(charcode) { - var fontCharCode, width, operatorList; + var fontCharCode, width, operatorList, disabled; var width = this.widths[charcode]; @@ -2451,11 +2461,14 @@ var Font = (function FontClosure() { unicodeChars = String.fromCharCode(unicodeChars); width = (isNum(width) ? width : this.defaultWidth) * this.widthMultiplier; + disabled = this.unicodeIsEnabled ? + !this.unicodeIsEnabled[fontCharCode] : false; return { fontChar: String.fromCharCode(fontCharCode), unicode: unicodeChars, width: width, + disabled: disabled, operatorList: operatorList }; }, From bcdf7b46c58bbd573efe00ce242dab50899908a7 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 25 Mar 2012 14:31:28 -0500 Subject: [PATCH 31/54] Fixing advance after disabled symbols --- src/canvas.js | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/canvas.js b/src/canvas.js index 2528a6b3b..e915c4a84 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -751,31 +751,31 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { x += Util.sign(current.fontMatrix[0]) * wordSpacing; continue; } - if (glyph.disabled) - continue; var char = glyph.fontChar; var charWidth = glyph.width * fontSize * 0.001 + Util.sign(current.fontMatrix[0]) * charSpacing; - var scaledX = x / fontSizeScale; - switch (textRenderingMode) { - default: // other unsupported rendering modes - case TextRenderingMode.FILL: - case TextRenderingMode.FILL_ADD_TO_PATH: - ctx.fillText(char, scaledX, 0); - break; - case TextRenderingMode.STROKE: - case TextRenderingMode.STROKE_ADD_TO_PATH: - ctx.strokeText(char, scaledX, 0); - break; - case TextRenderingMode.FILL_STROKE: - case TextRenderingMode.FILL_STROKE_ADD_TO_PATH: - ctx.fillText(char, scaledX, 0); - ctx.strokeText(char, scaledX, 0); - break; - case TextRenderingMode.INVISIBLE: - break; + if (!glyph.disabled) { + var scaledX = x / fontSizeScale; + switch (textRenderingMode) { + default: // other unsupported rendering modes + case TextRenderingMode.FILL: + case TextRenderingMode.FILL_ADD_TO_PATH: + ctx.fillText(char, scaledX, 0); + break; + case TextRenderingMode.STROKE: + case TextRenderingMode.STROKE_ADD_TO_PATH: + ctx.strokeText(char, scaledX, 0); + break; + case TextRenderingMode.FILL_STROKE: + case TextRenderingMode.FILL_STROKE_ADD_TO_PATH: + ctx.fillText(char, scaledX, 0); + ctx.strokeText(char, scaledX, 0); + break; + case TextRenderingMode.INVISIBLE: + break; + } } x += charWidth; From 851220074dc6ecd913e2e7156a403208708b6467 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 25 Mar 2012 16:30:44 -0500 Subject: [PATCH 32/54] Checking if it's a true symbolic truetype font --- src/fonts.js | 13 +++++++++++++ test/pdfs/preistabelle.pdf.link | 1 + test/test_manifest.json | 8 ++++++++ 3 files changed, 22 insertions(+) create mode 100644 test/pdfs/preistabelle.pdf.link diff --git a/src/fonts.js b/src/fonts.js index 500ef8780..6ef1970ed 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1958,6 +1958,19 @@ var Font = (function FontClosure() { glyphsRemoved++; } + // checking if it's a "true" symbolic font + if (this.isSymbolicFont) { + var minUnicode = 0xFFFF, maxUnicode = 0; + for (var i = 0, ii = glyphs.length; i < ii; i++) { + var unicode = glyphs[i].unicode; + minUnicode = Math.min(minUnicode, unicode); + maxUnicode = Math.max(maxUnicode, unicode); + } + // high byte must be the same for min and max unicodes + if ((maxUnicode & 0xFF00) != (minUnicode & 0xFF00)) + this.isSymbolicFont = false; + } + // heuristics: if removed more than 2 glyphs encoding WinAnsiEncoding // does not set properly if (glyphsRemoved > 2) { diff --git a/test/pdfs/preistabelle.pdf.link b/test/pdfs/preistabelle.pdf.link new file mode 100644 index 000000000..f48f8565c --- /dev/null +++ b/test/pdfs/preistabelle.pdf.link @@ -0,0 +1 @@ +http://www.fyve.de/downloads/Preistabelle_FYVE_Oktober_2010.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 73aa77cd9..92b50d9ed 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -480,6 +480,14 @@ "link": true, "type": "eq" }, + { "id": "preistabelle", + "file": "pdfs/preistabelle.pdf", + "md5": "d2f0b2086160d4f3d325c79a5dc1fb4d", + "rounds": 1, + "pageLimit": 2, + "link": true, + "type": "eq" + }, { "id": "issue925", "file": "pdfs/issue925.pdf", "md5": "f58fe943090aff89dcc8e771bc0db4c2", From a0a5c588941566d5f3b3979f6385595fae7c2c74 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Mon, 26 Mar 2012 16:33:00 -0400 Subject: [PATCH 33/54] Upgrading ShellJS, introducing 'makeref' --- external/shelljs/ChangeLog | 22 +++ external/shelljs/README.md | 61 ++++--- external/shelljs/package.json | 2 +- external/shelljs/shell.js | 295 ++++++++++++++++++++++++---------- make.js | 47 ++++-- 5 files changed, 304 insertions(+), 123 deletions(-) create mode 100644 external/shelljs/ChangeLog diff --git a/external/shelljs/ChangeLog b/external/shelljs/ChangeLog new file mode 100644 index 000000000..23ca5d0e0 --- /dev/null +++ b/external/shelljs/ChangeLog @@ -0,0 +1,22 @@ +2012.03.22, Version 0.0.4 + +* ls() and find() return arrays instead of hashes (Artur Adib) +* exec({silent:...}) overrides global silent() state (Artur Adib) + + +2012.03.21, Version 0.0.3 + +* Wildcard bug fix (Artur Adib) +* execSync() now uses dummy file I/O op to reduce CPU usage (Artur Adib) +* Minor fixes + + +2012.03.15, Version 0.0.2 + +* New methods: find(), test() (Artur Adib) +* Deprecated non-Unix methods: exists(), verbose() + + +2012.03.03, Version 0.0.2pre1 + +* First public release diff --git a/external/shelljs/README.md b/external/shelljs/README.md index 82f53533b..9f5386efe 100644 --- a/external/shelljs/README.md +++ b/external/shelljs/README.md @@ -1,6 +1,7 @@ # ShellJS - Unix shell commands for Node.js [![Build Status](https://secure.travis-ci.org/arturadib/shelljs.png)](http://travis-ci.org/arturadib/shelljs) -_This project is young and experimental. Use at your own risk._ ++ _This project is young and experimental. Use at your own risk._ ++ _Major API change as of v0.0.4: `ls()` and `find()` now return arrays._ ShellJS is a **portable** (Windows included) implementation of Unix shell commands on top of the Node.js API. You can use it to eliminate your shell script's dependency on Unix while still keeping its familiar and powerful commands. @@ -18,11 +19,11 @@ cp('-R', 'stuff/*', 'out/Release'); // Replace macros in each .js file cd('lib'); -for (file in ls('*.js')) { +ls('*.js').forEach(function(file) { sed('-i', 'BUILD_VERSION', 'v0.1.2', file); sed('-i', /.*REMOVE_THIS_LINE.*\n/, '', file); sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, cat('macro.js'), file); -} +}); cd('..'); // Run external tool synchronously @@ -73,11 +74,11 @@ target.docs = function() { cd(__dirname); mkdir('docs'); cd('lib'); - for (file in ls('*.js')) { + ls('*.js').forEach(function(file){ var text = grep('//@', file); // extract special comments text.replace('//@', ''); // remove comment tags text.to('docs/my_docs.md'); - } + }); } ``` @@ -128,9 +129,7 @@ ls('-R', '/users/me', '/tmp'); ls('-R', ['/users/me', '/tmp']); // same as above ``` -Returns list of files in the given path, or in current directory if no path provided. -For convenient iteration via `for (file in ls())`, the format returned is a hash object: -`{ 'file1':null, 'dir1/file2':null, ...}`. +Returns array of files in the given path, or in current directory if no path provided. #### find(path [,path ...]) #### find(path_array) @@ -139,18 +138,12 @@ Examples: ```javascript find('src', 'lib'); find(['src', 'lib']); // same as above -for (file in find('.')) { -if (!file.match(/\.js$/)) -continue; -// all files at this point end in '.js' -} +find('.').filter(function(file) { return file.match(/\.js$/); }) ``` -Returns list of all files (however deep) in the given paths. For convenient iteration -via `for (file in find(...))`, the format returned is a hash object: -`{ 'file1':null, 'dir1/file2':null, ...}`. +Returns array of all files (however deep) in the given paths. -The main difference with respect to `ls('-R', path)` is that the resulting file names +The main difference from `ls('-R', path)` is that the resulting file names include the base directories, e.g. `lib/resources/file1` instead of just `file1`. #### cp('[options ,] source [,source ...], dest') @@ -339,16 +332,34 @@ arguments `(code, output)`. Searches and returns string containing a writeable, platform-dependent temporary directory. Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). -#### exists(path [, path ...]) -#### exists(path_array) -Returns true if all the given paths exist. - #### error() Tests if error occurred in the last command. Returns `null` if no error occurred, otherwise returns string explaining the error -#### verbose() -Enables all output (default) +#### silent([state]) +Example: -#### silent() -Suppresses all output, except for explict `echo()` calls +```javascript +var silentState = silent(); +silent(true); +/* ... */ +silent(silentState); // restore old silent state +``` + +Suppresses all output if `state = true`. Returns state if no arguments given. + +## Deprecated + + +#### exists(path [, path ...]) +#### exists(path_array) + +_This function is being deprecated. Use `test()` instead._ + +Returns true if all the given paths exist. + +#### verbose() + +_This function is being deprecated. Use `silent(false) instead.`_ + +Enables all output (default) diff --git a/external/shelljs/package.json b/external/shelljs/package.json index 9499680e9..99c0f5e64 100644 --- a/external/shelljs/package.json +++ b/external/shelljs/package.json @@ -1,5 +1,5 @@ { "name": "shelljs" -, "version": "0.0.2pre1" +, "version": "0.0.5pre2" , "author": "Artur Adib " , "description": "Portable Unix shell commands for Node.js" , "keywords": ["unix", "shell", "makefile", "make", "jake", "synchronous"] diff --git a/external/shelljs/shell.js b/external/shelljs/shell.js index 95ddaa067..9c1181fb6 100644 --- a/external/shelljs/shell.js +++ b/external/shelljs/shell.js @@ -74,9 +74,7 @@ exports.pwd = wrap('pwd', _pwd); //@ ls('-R', ['/users/me', '/tmp']); // same as above //@ ``` //@ -//@ Returns list of files in the given path, or in current directory if no path provided. -//@ For convenient iteration via `for (file in ls())`, the format returned is a hash object: -//@ `{ 'file1':null, 'dir1/file2':null, ...}`. +//@ Returns array of files in the given path, or in current directory if no path provided. function _ls(options, paths) { options = parseOptions(options, { 'R': 'recursive', @@ -90,24 +88,30 @@ function _ls(options, paths) { else if (typeof paths === 'string') paths = [].slice.call(arguments, 1); - var hash = {}; + var list = []; - function pushHash(file, query) { + // Conditionally pushes file to list - returns true if pushed, false otherwise + // (e.g. prevents hidden files to be included unless explicitly told so) + function pushFile(file, query) { // hidden file? if (path.basename(file)[0] === '.') { // not explicitly asking for hidden files? if (!options.all && !(path.basename(query)[0] === '.' && path.basename(query).length > 1)) - return; + return false; } - hash[file] = null; + if (platform === 'win') + file = file.replace(/\\/g, '/'); + + list.push(file); + return true; } paths.forEach(function(p) { if (fs.existsSync(p)) { // Simple file? if (fs.statSync(p).isFile()) { - pushHash(p, p); + pushFile(p, p); return; // continue } @@ -115,14 +119,17 @@ function _ls(options, paths) { if (fs.statSync(p).isDirectory()) { // Iterate over p contents fs.readdirSync(p).forEach(function(file) { - pushHash(file, p); + if (!pushFile(file, p)) + return; - // Recursive - var oldDir = _pwd(); - _cd('', p); - if (fs.statSync(file).isDirectory() && options.recursive) - hash = extend(hash, _ls('-R', file+'/*')); - _cd('', oldDir); + // Recursive? + if (options.recursive) { + var oldDir = _pwd(); + _cd('', p); + if (fs.statSync(file).isDirectory()) + list = list.concat(_ls('-R'+(options.all?'a':''), file+'/*')); + _cd('', oldDir); + } }); return; // continue } @@ -137,17 +144,20 @@ function _ls(options, paths) { // Escape special regular expression chars var regexp = basename.replace(/(\^|\$|\(|\)|\<|\>|\[|\]|\{|\}|\.|\+|\?)/g, '\\$1'); // Translates wildcard into regex - regexp = '^' + regexp.replace(/\*/g, '.*'); + regexp = '^' + regexp.replace(/\*/g, '.*') + '$'; // Iterate over directory contents fs.readdirSync(dirname).forEach(function(file) { if (file.match(new RegExp(regexp))) { - pushHash(path.normalize(dirname+'/'+file), basename); + if (!pushFile(path.normalize(dirname+'/'+file), basename)) + return; - // Recursive - var pp = dirname + '/' + file; - if (fs.statSync(pp).isDirectory() && options.recursive) - hash = extend(hash, _ls('-R', pp+'/*')); - } + // Recursive? + if (options.recursive) { + var pp = dirname + '/' + file; + if (fs.statSync(pp).isDirectory()) + list = list.concat(_ls('-R'+(options.all?'a':''), pp+'/*')); + } // recursive + } // if file matches }); // forEach return; } @@ -155,7 +165,7 @@ function _ls(options, paths) { error('no such file or directory: ' + p, true); }); - return hash; + return list; }; exports.ls = wrap('ls', _ls); @@ -168,16 +178,10 @@ exports.ls = wrap('ls', _ls); //@ ```javascript //@ find('src', 'lib'); //@ find(['src', 'lib']); // same as above -//@ for (file in find('.')) { -//@ if (!file.match(/\.js$/)) -//@ continue; -//@ // all files at this point end in '.js' -//@ } +//@ find('.').filter(function(file) { return file.match(/\.js$/); }); //@ ``` //@ -//@ Returns list of all files (however deep) in the given paths. For convenient iteration -//@ via `for (file in find(...))`, the format returned is a hash object: -//@ `{ 'file1':null, 'dir1/file2':null, ...}`. +//@ Returns array of all files (however deep) in the given paths. //@ //@ The main difference from `ls('-R', path)` is that the resulting file names //@ include the base directories, e.g. `lib/resources/file1` instead of just `file1`. @@ -189,21 +193,28 @@ function _find(options, paths) { else if (typeof paths === 'string') paths = [].slice.call(arguments, 1); - var hash = {}; + var list = []; + + function pushFile(file) { + if (platform === 'win') + file = file.replace(/\\/g, '/'); + list.push(file); + } // why not simply do ls('-R', paths)? because the output wouldn't give the base dirs // to get the base dir in the output, we need instead ls('-R', 'dir/*') for every directory - paths.forEach(function(file){ - hash[file] = null; + paths.forEach(function(file) { + pushFile(file); if (fs.statSync(file).isDirectory()) { - for (subfile in _ls('-Ra', file+'/*')) - hash[subfile] = null; + _ls('-Ra', file+'/*').forEach(function(subfile) { + pushFile(subfile); + }); } }); - return hash; + return list; } exports.find = wrap('find', _find); @@ -347,9 +358,20 @@ function _rm(options, files) { // Remove simple file if (fs.statSync(file).isFile()) { - fs.unlinkSync(file); + + // Do not check for file writing permissions + if (options.force) { + _unlinkSync(file); + return; + } + + if (isWriteable(file)) + _unlinkSync(file); + else + error('permission denied: '+file, true); + return; - } + } // simple file // Path is an existing directory, but no -r flag given if (fs.statSync(file).isDirectory() && !options.recursive) { @@ -359,7 +381,7 @@ function _rm(options, files) { // Recursively remove existing directory if (fs.statSync(file).isDirectory() && options.recursive) { - rmdirSyncRecursive(file); + rmdirSyncRecursive(file, options.force); } }); // forEach(file) }; // rm @@ -582,7 +604,11 @@ function _to(options, file) { if (!fs.existsSync( path.dirname(file) )) error('no such file or directory: ' + path.dirname(file)); - fs.writeFileSync(file, this.toString(), 'utf8'); + try { + fs.writeFileSync(file, this.toString(), 'utf8'); + } catch(e) { + error('could not write to file (code '+e.code+'): '+file, true); + } }; // In the future, when Proxies are default, we can add methods like `.to()` to primitive strings. // For now, this is a dummy function to bookmark places we need such strings @@ -793,7 +819,7 @@ function _exec(command, options, callback) { } options = extend({ - silent: false, + silent: state.silent, async: false }, options); @@ -822,11 +848,52 @@ exports.exec = wrap('exec', _exec, {notUnix:true}); //@ Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). exports.tempdir = wrap('tempdir', tempDir); + +//@ +//@ #### error() +//@ Tests if error occurred in the last command. Returns `null` if no error occurred, +//@ otherwise returns string explaining the error +exports.error = function() { + return state.error; +} + +//@ +//@ #### silent([state]) +//@ Example: +//@ +//@ ```javascript +//@ var silentState = silent(); +//@ silent(true); +//@ /* ... */ +//@ silent(silentState); // restore old silent state +//@ ``` +//@ +//@ Suppresses all output if `state = true`. Returns state if no arguments given. +exports.silent = function(_state) { + if (typeof _state !== 'boolean') + return state.silent; + + state.silent = _state; +} + + +//@ +//@ ## Deprecated +//@ + + + + //@ //@ #### exists(path [, path ...]) //@ #### exists(path_array) +//@ +//@ _This function is being deprecated. Use `test()` instead._ +//@ //@ Returns true if all the given paths exist. function _exists(options, paths) { + deprecate('exists', 'Use test() instead.'); + if (!paths) error('no paths given'); @@ -844,32 +911,19 @@ function _exists(options, paths) { }; exports.exists = wrap('exists', _exists); -//@ -//@ #### error() -//@ Tests if error occurred in the last command. Returns `null` if no error occurred, -//@ otherwise returns string explaining the error -exports.error = function() { - return state.error; -} //@ //@ #### verbose() +//@ +//@ _This function is being deprecated. Use `silent(false) instead.`_ +//@ //@ Enables all output (default) exports.verbose = function() { + deprecate('verbose', 'Use silent(false) instead.'); + state.silent = false; } -//@ -//@ #### silent() -//@ Suppresses all output, except for explict `echo()` calls -exports.silent = function() { - state.silent = true; -} - - - - - @@ -889,6 +943,10 @@ function log() { console.log.apply(this, arguments); } +function deprecate(what, msg) { + console.log('*** ShellJS.'+what+': This function is deprecated.', msg); +} + function write(msg) { if (!state.silent) process.stdout.write(msg); @@ -984,10 +1042,22 @@ function copyFileSync(srcFile, destFile) { var BUF_LENGTH = 64*1024, buf = new Buffer(BUF_LENGTH), - fdr = fs.openSync(srcFile, 'r'), - fdw = fs.openSync(destFile, 'w'), bytesRead = BUF_LENGTH, - pos = 0; + pos = 0, + fdr = null, + fdw = null; + + try { + fdr = fs.openSync(srcFile, 'r'); + } catch(e) { + error('copyFileSync: could not read src file ('+srcFile+')'); + } + + try { + fdw = fs.openSync(destFile, 'w'); + } catch(e) { + error('copyFileSync: could not write to dest file (code='+e.code+'):'+destFile); + } while (bytesRead === BUF_LENGTH) { bytesRead = fs.readSync(fdr, buf, 0, BUF_LENGTH, pos); @@ -1050,28 +1120,42 @@ function cpdirSyncRecursive(sourceDir, destDir, opts) { // // Licensed under the MIT License // http://www.opensource.org/licenses/mit-license.php -function rmdirSyncRecursive(dir) { +function rmdirSyncRecursive(dir, force) { var files; files = fs.readdirSync(dir); // Loop through and delete everything in the sub-tree after checking it for(var i = 0; i < files.length; i++) { - var currFile = fs.lstatSync(dir + "/" + files[i]); + var file = dir + "/" + files[i], + currFile = fs.lstatSync(file); - if(currFile.isDirectory()) // Recursive function back to the beginning - rmdirSyncRecursive(dir + "/" + files[i]); + if(currFile.isDirectory()) { // Recursive function back to the beginning + rmdirSyncRecursive(file, force); + } - else if(currFile.isSymbolicLink()) // Unlink symlinks - fs.unlinkSync(dir + "/" + files[i]); + else if(currFile.isSymbolicLink()) { // Unlink symlinks + if (force || isWriteable(file)) + _unlinkSync(file); + } else // Assume it's a file - perhaps a try/catch belongs here? - fs.unlinkSync(dir + "/" + files[i]); + if (force || isWriteable(file)) + _unlinkSync(file); } // Now that we know everything in the sub-tree has been deleted, we can delete the main directory. // Huzzah for the shopkeep. - return fs.rmdirSync(dir); + + var result; + try { + result = fs.rmdirSync(dir); + } catch(e) { + if (e.code === 'ENOTEMPTY') + error('directory not empty: ' + dir, true); + } + + return result; }; // rmdirSyncRecursive // Recursively creates 'dir' @@ -1118,7 +1202,7 @@ function writeableDir(dir) { var testFile = dir+'/'+randomFileName(); try { fs.writeFileSync(testFile, ' '); - fs.unlinkSync(testFile); + _unlinkSync(testFile); return dir; } catch (e) { return false; @@ -1150,7 +1234,8 @@ function tempDir() { // Wrapper around exec() to enable echoing output to console in real time function execAsync(cmd, opts, callback) { - var output = ''; + var output = '', + silent = 'silent' in opts ? opts.silent : state.silent; var c = child.exec(cmd, {env: process.env}, function(err) { if (callback) @@ -1159,13 +1244,13 @@ function execAsync(cmd, opts, callback) { c.stdout.on('data', function(data) { output += data; - if (!opts.silent) + if (!silent) write(data); }); c.stderr.on('data', function(data) { output += data; - if (!opts.silent) + if (!silent) write(data); }); } @@ -1178,16 +1263,17 @@ function execAsync(cmd, opts, callback) { function execSync(cmd, opts) { var stdoutFile = path.resolve(tempDir()+'/'+randomFileName()), codeFile = path.resolve(tempDir()+'/'+randomFileName()), - scriptFile = path.resolve(tempDir()+'/'+randomFileName()); + scriptFile = path.resolve(tempDir()+'/'+randomFileName()), + sleepFile = path.resolve(tempDir()+'/'+randomFileName()); var options = extend({ - silent: false + silent: state.silent }, opts); var previousStdoutContent = ''; // Echoes stdout changes from running process, if not silent function updateStdout() { - if (state.silent || options.silent || !fs.existsSync(stdoutFile)) + if (options.silent || !fs.existsSync(stdoutFile)) return; var stdoutContent = fs.readFileSync(stdoutFile, 'utf8'); @@ -1214,9 +1300,9 @@ function execSync(cmd, opts) { fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0'); \ });"; - if (fs.existsSync(scriptFile)) fs.unlinkSync(scriptFile); - if (fs.existsSync(stdoutFile)) fs.unlinkSync(stdoutFile); - if (fs.existsSync(codeFile)) fs.unlinkSync(codeFile); + if (fs.existsSync(scriptFile)) _unlinkSync(scriptFile); + if (fs.existsSync(stdoutFile)) _unlinkSync(stdoutFile); + if (fs.existsSync(codeFile)) _unlinkSync(codeFile); fs.writeFileSync(scriptFile, script); child.exec('node '+scriptFile, { @@ -1225,8 +1311,11 @@ function execSync(cmd, opts) { }); // The wait loop - while (!fs.existsSync(codeFile)) { updateStdout(); }; - while (!fs.existsSync(stdoutFile)) { updateStdout(); }; + // sleepFile is used as a dummy I/O op to mitigate unnecessary CPU usage + // (tried many I/O sync ops, writeFileSync() seems to be only one that is effective in reducing + // CPU usage, though apparently not so much on Windows) + while (!fs.existsSync(codeFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); }; + while (!fs.existsSync(stdoutFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); }; // At this point codeFile exists, but it's not necessarily flushed yet. // Keep reading it until it is. @@ -1236,9 +1325,10 @@ function execSync(cmd, opts) { var stdout = fs.readFileSync(stdoutFile, 'utf8'); - fs.unlinkSync(scriptFile); - fs.unlinkSync(stdoutFile); - fs.unlinkSync(codeFile); + _unlinkSync(scriptFile); + _unlinkSync(stdoutFile); + _unlinkSync(codeFile); + _unlinkSync(sleepFile); // True if successful, false if not var obj = { @@ -1257,8 +1347,9 @@ function expand(list) { list.forEach(function(listEl) { // Wildcard present? if (listEl.search(/\*/) > -1) { - for (file in _ls('', listEl)) + _ls('', listEl).forEach(function(file) { expanded.push(file); + }); } else { expanded.push(listEl); } @@ -1290,3 +1381,33 @@ function extend(target) { return target; } + +// Normalizes _unlinkSync() across platforms to match Unix behavior, i.e. +// file can be unlinked even if it's read-only, see joyent/node#3006 +function _unlinkSync(file) { + try { + fs.unlinkSync(file); + } catch(e) { + // Try to override file permission + if (e.code === 'EPERM') { + fs.chmodSync(file, '0666'); + fs.unlinkSync(file); + } else { + throw e; + } + } +} + +// Hack to determine if file has write permissions for current user +// Avoids having to check user, group, etc, but it's probably slow +function isWriteable(file) { + var writePermission = true; + try { + var __fd = fs.openSync(file, 'a'); + fs.closeSync(__fd); + } catch(e) { + writePermission = false; + } + + return writePermission; +} diff --git a/make.js b/make.js index 33771aeb7..973cb445a 100755 --- a/make.js +++ b/make.js @@ -99,7 +99,7 @@ target.bundle = function() { 'jpx.js', 'bidi.js']; - if (!exists(BUILD_DIR)) + if (!test('-d', BUILD_DIR)) mkdir(BUILD_DIR); cd('src'); @@ -142,10 +142,10 @@ target.pagesrepo = function() { echo(); echo('### Creating fresh clone of gh-pages'); - if (!exists(BUILD_DIR)) + if (!test('-d', BUILD_DIR)) mkdir(BUILD_DIR); - if (!exists(GH_PAGES_DIR)) { + if (!test('-d', GH_PAGES_DIR)) { echo(); echo('Cloning project repo...'); echo('(This operation can take a while, depending on network conditions)'); @@ -277,10 +277,10 @@ target.firefox = function() { // We don't need pdf.js anymore since its inlined rm('-Rf', FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR); // Remove '.DS_Store' and other hidden files - for (file in find(FIREFOX_BUILD_DIR)) { + find(FIREFOX_BUILD_DIR).forEach(function(file) { if (file.match(/^\./)) rm('-f', file); - } + }); // Update the build version number sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/install.rdf'); @@ -304,10 +304,10 @@ target.firefox = function() { // List all files for mozilla-central cd(FIREFOX_BUILD_DIR); var extensionFiles = ''; - for (file in find(FIREFOX_MC_EXTENSION_FILES)) { + find(FIREFOX_MC_EXTENSION_FILES).forEach(function(file){ if (test('-f', file)) extensionFiles += file+'\n'; - } + }); extensionFiles.to('extension-files'); }; @@ -357,7 +357,7 @@ target.chrome = function() { // target.test = function() { target.browsertest(); - target.unittest(); +// target.unittest(); }; // @@ -371,14 +371,14 @@ target.browsertest = function() { var PDF_TEST = env['PDF_TEST'] || 'test_manifest.json', PDF_BROWSERS = env['PDF_BROWSERS'] || 'resources/browser_manifests/browser_manifest.json'; - if (!exists('test/' + PDF_BROWSERS)) { + if (!test('-f', 'test/' + PDF_BROWSERS)) { echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.'); echo('Try copying one of the examples in test/resources/browser_manifests/'); exit(1); } cd('test'); - exec(PYTHON_BIN + ' test.py --reftest --browserManifestFile=' + PDF_BROWSERS + + exec(PYTHON_BIN + ' -u test.py --reftest --browserManifestFile=' + PDF_BROWSERS + ' --manifestFile=' + PDF_TEST, {async: true}); }; @@ -390,10 +390,37 @@ target.unittest = function() { echo(); echo('### Running unit tests'); + if (!which('make')) { + echo('make not found. Skipping unit tests...'); + return; + } + cd('test/unit'); exec('make', {async: true}); }; +// +// make makeref +// +target.makeref = function() { + cd(ROOT_DIR); + echo(); + echo('### Creating reference images'); + + var PDF_TEST = env['PDF_TEST'] || 'test_manifest.json', + PDF_BROWSERS = env['PDF_BROWSERS'] || 'resources/browser_manifests/browser_manifest.json'; + + if (!test('-f', 'test/' + PDF_BROWSERS)) { + echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.'); + echo('Try copying one of the examples in test/resources/browser_manifests/'); + exit(1); + } + + cd('test'); + exec(PYTHON_BIN + ' -u test.py --masterMode --noPrompts --browserManifestFile=' + PDF_BROWSERS, + {async: true}); +}; + /////////////////////////////////////////////////////////////////////////////////////////// // From edc169462062ed5fedb70f56263f57f057d57683 Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Mon, 26 Mar 2012 23:48:04 +0200 Subject: [PATCH 34/54] Now also fetch 'Document Info Dictionary', and expose 'raw' metadata attributes --- src/core.js | 22 +++++++++++++++------- src/metadata.js | 20 +------------------- web/viewer.js | 15 +++++++++++++-- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/core.js b/src/core.js index ecc2c94a5..890563f4e 100644 --- a/src/core.js +++ b/src/core.js @@ -587,13 +587,15 @@ var PDFDocModel = (function PDFDocModelClosure() { this.mainXRefEntriesOffset); this.xref = xref; this.catalog = new Catalog(xref); - if (xref.trailer && xref.trailer.has('ID')) { - var fileID = ''; - var id = xref.fetchIfRef(xref.trailer.get('ID'))[0]; - id.split('').forEach(function(el) { - fileID += Number(el.charCodeAt(0)).toString(16); - }); - this.fileID = fileID; + if (xref.trailer) { + if (xref.trailer.has('ID')) { + var fileID = ''; + var id = xref.fetchIfRef(xref.trailer.get('ID'))[0]; + id.split('').forEach(function(el) { + fileID += Number(el.charCodeAt(0)).toString(16); + }); + this.fileID = fileID; + } } }, get numPages() { @@ -602,6 +604,11 @@ var PDFDocModel = (function PDFDocModelClosure() { // shadow the prototype getter return shadow(this, 'numPages', num); }, + getDocumentInfo: function pdfDocGetDocumentInfo() { + if (this.xref.trailer.has('Info')) { + return this.xref.fetch(this.xref.trailer.get('Info')); + } + }, getFingerprint: function pdfDocGetFingerprint() { if (this.fileID) { return this.fileID; @@ -645,6 +652,7 @@ var PDFDoc = (function PDFDocClosure() { this.stream = stream; this.pdfModel = new PDFDocModel(stream); this.fingerprint = this.pdfModel.getFingerprint(); + this.info = this.pdfModel.getDocumentInfo(); this.catalog = this.pdfModel.catalog; this.objs = new PDFObjects(); diff --git a/src/metadata.js b/src/metadata.js index 68b19764b..a46077f56 100644 --- a/src/metadata.js +++ b/src/metadata.js @@ -44,25 +44,7 @@ var Metadata = (function MetadataClosure() { for (ii = 0, iLength = entries.length; ii < iLength; ii++) { var entry = entries[ii]; var name = entry.nodeName.toLowerCase(); - var entryName = name.split(':'); - entryName = (entryName.length > 1) ? entryName[1] : entryName[0]; - switch (name) { - case 'pdf:moddate': - case 'xap:createdate': - case 'xap:metadatadate': - case 'xap:modifydate': - this.metadata[entryName] = new Date(entry.textContent.trim()); - break; - - default: - // For almost all entries we just add them to the metadata object - if (this.metadata[entryName]) { - this.metadata[name] = entry.textContent.trim(); - } else { - this.metadata[entryName] = entry.textContent.trim(); - } - break; - } + this.metadata[name] = entry.textContent.trim(); } } }, diff --git a/web/viewer.js b/web/viewer.js index 68577ad34..838de20d3 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -501,12 +501,23 @@ var PDFView = { } var metadata = pdf.catalog.metadata; + var info = pdf.info; + var pdfTitle; + if (metadata) { this.metadata = metadata = new Metadata(metadata); - if (metadata.has('title')) { - document.title = metadata.get('title'); + if (metadata.has('dc:title')) { + pdfTitle = metadata.get('dc:title'); } } + + if (info && info.has('Title') && !pdfTitle) { + pdfTitle = info.get('Title'); + } + + if (pdfTitle) { + document.title = pdfTitle; + } }, setHash: function pdfViewSetHash(hash) { From 878854f7cfc4809aa35303453c4b51b54eb894af Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Mon, 26 Mar 2012 23:53:51 +0200 Subject: [PATCH 35/54] wrong filename in make.js --- make.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make.js b/make.js index 69732667f..19dea7b82 100755 --- a/make.js +++ b/make.js @@ -98,7 +98,7 @@ target.bundle = function() { '../external/jpgjs/jpg.js', 'jpx.js', 'bidi.js', - 'metadata-js']; + 'metadata.js']; if (!exists(BUILD_DIR)) mkdir(BUILD_DIR); From 94a155884397bc7d7b4e5fb6ad1271fb1bef36f3 Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Tue, 27 Mar 2012 00:05:14 +0200 Subject: [PATCH 36/54] refactor and shadow --- src/obj.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/obj.js b/src/obj.js index 21f20c3fb..2eb9c6f1d 100644 --- a/src/obj.js +++ b/src/obj.js @@ -113,24 +113,19 @@ var Catalog = (function CatalogClosure() { Catalog.prototype = { get metadata() { var ref = this.catDict.get('Metadata'); - if (!ref) { - return null; - } - - var stream = this.xref.fetch(ref); - var dict = stream.dict; - if (isDict(dict)) { - var type = dict.get('Type'); - var subtype = dict.get('Subtype'); + var stream = this.xref.fetchIfRef(ref); + var metadata; + if (stream && isDict(stream.dict)) { + var type = stream.dict.get('Type'); + var subtype = stream.dict.get('Subtype'); if (isName(type) && isName(subtype) && type.name === 'Metadata' && subtype.name === 'XML') { - var metadata = stringToPDFString(bytesToString(stream.getBytes())); - return metadata; + metadata = stringToPDFString(bytesToString(stream.getBytes())); } } - return null; + return shadow(this, 'metadata', metadata); }, get toplevelPagesDict() { var pagesObj = this.catDict.get('Pages'); From 4703a6cfcd4d989cc5faff3165b44df5f927c645 Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Tue, 27 Mar 2012 00:14:59 +0200 Subject: [PATCH 37/54] refactor getFingerprint --- src/core.js | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/core.js b/src/core.js index 890563f4e..dffdbd44e 100644 --- a/src/core.js +++ b/src/core.js @@ -587,16 +587,6 @@ var PDFDocModel = (function PDFDocModelClosure() { this.mainXRefEntriesOffset); this.xref = xref; this.catalog = new Catalog(xref); - if (xref.trailer) { - if (xref.trailer.has('ID')) { - var fileID = ''; - var id = xref.fetchIfRef(xref.trailer.get('ID'))[0]; - id.split('').forEach(function(el) { - fileID += Number(el.charCodeAt(0)).toString(16); - }); - this.fileID = fileID; - } - } }, get numPages() { var linearization = this.linearization; @@ -610,20 +600,25 @@ var PDFDocModel = (function PDFDocModelClosure() { } }, getFingerprint: function pdfDocGetFingerprint() { - if (this.fileID) { - return this.fileID; + var xref = this.xref, fileID; + if (xref.trailer.has('ID')) { + fileID = ''; + var id = xref.fetchIfRef(xref.trailer.get('ID'))[0]; + id.split('').forEach(function(el) { + fileID += Number(el.charCodeAt(0)).toString(16); + }); } else { // If we got no fileID, then we generate one, // from the first 100 bytes of PDF var data = this.stream.bytes.subarray(0, 100); var hash = calculateMD5(data, 0, data.length); - var strHash = ''; + fileID = ''; for (var i = 0, length = hash.length; i < length; i++) { - strHash += Number(hash[i]).toString(16); + fileID += Number(hash[i]).toString(16); } - - return strHash; } + + return shadow(this, 'getFingerprint', fileID); }, getPage: function pdfDocGetPage(n) { return this.catalog.getPage(n); From e283a60d7b51b11a2dd018983d2fd80eca7f871c Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Tue, 27 Mar 2012 00:16:16 +0200 Subject: [PATCH 38/54] return and shadow for getDocumentInfo --- src/core.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core.js b/src/core.js index dffdbd44e..3e10e6586 100644 --- a/src/core.js +++ b/src/core.js @@ -595,9 +595,12 @@ var PDFDocModel = (function PDFDocModelClosure() { return shadow(this, 'numPages', num); }, getDocumentInfo: function pdfDocGetDocumentInfo() { + var info; if (this.xref.trailer.has('Info')) { - return this.xref.fetch(this.xref.trailer.get('Info')); + info = this.xref.fetch(this.xref.trailer.get('Info')); } + + return shadow(this, 'getDocumentInfo', info); }, getFingerprint: function pdfDocGetFingerprint() { var xref = this.xref, fileID; From 2bb5a9e5450f7f6af32c00667587b0987494d43c Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Tue, 27 Mar 2012 00:17:57 +0200 Subject: [PATCH 39/54] moving \!pdfTitle to front --- web/viewer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/viewer.js b/web/viewer.js index 838de20d3..cf30d2978 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -511,7 +511,7 @@ var PDFView = { } } - if (info && info.has('Title') && !pdfTitle) { + if (!pdfTitle && info && info.has('Title')) { pdfTitle = info.get('Title'); } From 7ee3d2d9a511cdb344796c9910fe7ffc493d9ea3 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Mon, 26 Mar 2012 21:43:51 -0500 Subject: [PATCH 40/54] Fixing refimages --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 51a7f4e98..2c706f4ac 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # PDF.JS - + pdf.js is an HTML5 technology experiment that explores building a faithful and efficient Portable Document Format (PDF) renderer without native code From a094dd4746ad4572d5c0aa321cdf290066bcadc0 Mon Sep 17 00:00:00 2001 From: gigaherz Date: Tue, 27 Mar 2012 09:13:32 +0200 Subject: [PATCH 41/54] Simplified the ProgressBar a bit: made the div fixed-size, removed the unnecessary '.remaining' div, used percent size for the '.progress' div. --- web/viewer.css | 14 +++++++------- web/viewer.html | 2 +- web/viewer.js | 21 +++++++-------------- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/web/viewer.css b/web/viewer.css index e8e986804..9a0cf388c 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -397,18 +397,22 @@ canvas { } #loadingBar { + background-color: #333; display: inline-block; border: 1px solid black; clear: both; margin:0px; line-height: 0; border-radius: 4px; + width: 15em; + height: 1.5em; } #loadingBar .progress { background-color: green; display: inline-block; - + float: left; + background: #b4e391; background: -moz-linear-gradient(top, #b4e391 0%, #61c419 50%, #b4e391 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b4e391), color-stop(50%,#61c419), color-stop(100%,#b4e391)); @@ -419,13 +423,9 @@ canvas { border-top-left-radius: 3px; border-bottom-left-radius: 3px; -} -#loadingBar .remaining { - background-color: #333; - display: inline-block; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; + width: 0%; + height: 100%; } #PDFBug { diff --git a/web/viewer.html b/web/viewer.html index 6528d484f..8b59e4ebf 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -144,7 +144,7 @@
Loading... 0%
-
+
diff --git a/web/viewer.js b/web/viewer.js index 3f6fef957..49a6cd4a3 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -36,29 +36,24 @@ var ProgressBar = (function ProgressBarClosure() { function ProgressBar(id, opts) { // Fetch the sub-elements for later - this.progressDiv = document.querySelector(id + ' .progress'); - this.remainingDiv = document.querySelector(id + ' .remaining'); + this.div = document.querySelector(id + ' .progress'); // Get options, with sensible defaults - this.height = opts.height || 1; - this.width = opts.width || 15; - this.units = opts.units || 'em'; + this.height = opts.height || 100; + this.width = opts.width || 100; + this.units = opts.units || '%'; this.percent = opts.percent || 0; // Initialize heights - this.progressDiv.style.height = this.height + this.units; - this.remainingDiv.style.height = this.height + this.units; + this.div.style.height = this.height + this.units; } ProgressBar.prototype = { - constructor: ProgressBar, updateBar: function ProgressBar_updateBar() { var progressSize = this.width * this._percent / 100; - var remainingSize = (this.width - progressSize); - this.progressDiv.style.width = progressSize + this.units; - this.remainingDiv.style.width = remainingSize + this.units; + this.div.style.width = progressSize + this.units; }, get percent() { @@ -307,10 +302,8 @@ var PDFView = { open: function pdfViewOpen(url, scale) { document.title = this.url = url; - // FIXME: Probably needs a better place to get initialized if (!PDFView.loadingBar) { - PDFView.loadingBar = new ProgressBar('#loadingBar', { - width: 15, height: 1.5, units: 'em'}); + PDFView.loadingBar = new ProgressBar('#loadingBar', {}); } var self = this; From a84fbb9eb272009d3855cb5dd648f81da839efbc Mon Sep 17 00:00:00 2001 From: Kalervo Kujala Date: Tue, 27 Mar 2012 14:27:49 +0300 Subject: [PATCH 42/54] Fix a couple of closure names. --- src/core.js | 2 +- src/fonts.js | 2 +- src/image.js | 2 +- src/jpx.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core.js b/src/core.js index ecc2c94a5..9c671960e 100644 --- a/src/core.js +++ b/src/core.js @@ -785,7 +785,7 @@ var PDFDoc = (function PDFDocClosure() { error('Only 3 component or 1 component can be returned'); var img = new Image(); - img.onload = (function jpegImageLoaderOnload() { + img.onload = (function messageHandler_onloadClosure() { var width = img.width; var height = img.height; var size = width * height; diff --git a/src/fonts.js b/src/fonts.js index 6ef1970ed..b756ff2ee 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -4247,7 +4247,7 @@ var CFFFDSelect = (function CFFFDSelectClosure() { // Helper class to keep track of where an offset is within the data and helps // filling in that offset once it's known. -var CFFOffsetTracker = (function CFFOffsetTracker() { +var CFFOffsetTracker = (function CFFOffsetTrackerClosure() { function CFFOffsetTracker() { this.offsets = {}; } diff --git a/src/image.js b/src/image.js index 6e7ab2020..7c23a3426 100644 --- a/src/image.js +++ b/src/image.js @@ -365,7 +365,7 @@ var PDFImage = (function PDFImageClosure() { function loadJpegStream(id, imageData, objs) { var img = new Image(); - img.onload = (function jpegImageLoaderOnload() { + img.onload = (function loadJpegStream_onloadClosure() { objs.resolve(id, img); }); img.src = 'data:image/jpeg;base64,' + window.btoa(imageData); diff --git a/src/jpx.js b/src/jpx.js index b420b04e1..7a13da0b5 100644 --- a/src/jpx.js +++ b/src/jpx.js @@ -159,7 +159,7 @@ var JpxImage = (function JpxImageClosure() { })(); // Implements C.3. Arithmetic decoding procedures - var ArithmeticDecoder = (function arithmeticDecoderClosure() { + var ArithmeticDecoder = (function ArithmeticDecoderClosure() { var QeTable = [ {qe: 0x5601, nmps: 1, nlps: 1, switchFlag: 1}, {qe: 0x3401, nmps: 2, nlps: 6, switchFlag: 0}, From eda1fe74f4cd8979226444f0e99cc120f563cc2a Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Tue, 27 Mar 2012 11:48:47 -0500 Subject: [PATCH 43/54] Remove scrollTo on unload --- web/viewer.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/viewer.js b/web/viewer.js index 67ef67e97..0e6fdb7fe 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -1195,10 +1195,6 @@ window.addEventListener('load', function webViewerLoad(evt) { sidebarScrollView.addEventListener('scroll', updateThumbViewArea, true); }, true); -window.addEventListener('unload', function webViewerUnload(evt) { - window.scrollTo(0, 0); -}, true); - /** * Render the next not yet visible page already such that it is * hopefully ready once the user scrolls to it. From e6277784f1b452d7018a28abe5824b0ec8a2bb8e Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Tue, 27 Mar 2012 22:32:35 +0200 Subject: [PATCH 44/54] fix to follow syntax style guidelines --- src/core.js | 3 +-- web/viewer.js | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/core.js b/src/core.js index 3e10e6586..7c996fc4a 100644 --- a/src/core.js +++ b/src/core.js @@ -596,9 +596,8 @@ var PDFDocModel = (function PDFDocModelClosure() { }, getDocumentInfo: function pdfDocGetDocumentInfo() { var info; - if (this.xref.trailer.has('Info')) { + if (this.xref.trailer.has('Info')) info = this.xref.fetch(this.xref.trailer.get('Info')); - } return shadow(this, 'getDocumentInfo', info); }, diff --git a/web/viewer.js b/web/viewer.js index cf30d2978..af34f6192 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -506,18 +506,16 @@ var PDFView = { if (metadata) { this.metadata = metadata = new Metadata(metadata); - if (metadata.has('dc:title')) { + + if (metadata.has('dc:title')) pdfTitle = metadata.get('dc:title'); - } } - if (!pdfTitle && info && info.has('Title')) { + if (!pdfTitle && info && info.has('Title')) pdfTitle = info.get('Title'); - } - if (pdfTitle) { + if (pdfTitle) document.title = pdfTitle; - } }, setHash: function pdfViewSetHash(hash) { From 89c1873fd8d4b89a4d7cfd054b3f8c3f4f7374dc Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Tue, 27 Mar 2012 22:37:02 +0200 Subject: [PATCH 45/54] more one-line fixes --- src/metadata.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/metadata.js b/src/metadata.js index a46077f56..ba87f7b84 100644 --- a/src/metadata.js +++ b/src/metadata.js @@ -18,27 +18,24 @@ var Metadata = (function MetadataClosure() { var rdf = doc.documentElement; if (rdf.tagName.toLowerCase() !== 'rdf:rdf') { // Wrapped in rdf = rdf.firstChild; - while (rdf.nodeName && rdf.nodeName.toLowerCase() !== 'rdf:rdf') { + while (rdf.nodeName && rdf.nodeName.toLowerCase() !== 'rdf:rdf') rdf = rdf.nextSibling; - } } - if (rdf.nodeName.toLowerCase() !== 'rdf:rdf' || !rdf.hasChildNodes()) { + + if (rdf.nodeName.toLowerCase() !== 'rdf:rdf' || !rdf.hasChildNodes()) return; - } var childNodes = rdf.childNodes, desc, namespace, entries, entry; for (var i = 0, length = childNodes.length; i < length; i++) { desc = childNodes[i]; - if (desc.nodeName.toLowerCase() !== 'rdf:description') { + if (desc.nodeName.toLowerCase() !== 'rdf:description') continue; - } entries = []; for (var ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) { - if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') { + if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') entries.push(desc.childNodes[ii]); - } } for (ii = 0, iLength = entries.length; ii < iLength; ii++) { From 0d216e62fed26096823fb421ef0eb3c940c4372b Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 27 Mar 2012 21:00:27 -0400 Subject: [PATCH 46/54] fix wnv_chinese link --- test/pdfs/wnv_chinese.pdf.link | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pdfs/wnv_chinese.pdf.link b/test/pdfs/wnv_chinese.pdf.link index fbbc81760..0bd2af8f6 100644 --- a/test/pdfs/wnv_chinese.pdf.link +++ b/test/pdfs/wnv_chinese.pdf.link @@ -1 +1 @@ -http://www.cdc.gov/ncidod/dvbid/westnile/languages/chinese.pdf +http://web.archive.org/web/20110623114753/http://www.cdc.gov/ncidod/dvbid/westnile/languages/chinese.pdf From 1181ef2778d995f39a5b36497d83875ca0735821 Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Wed, 28 Mar 2012 19:15:59 +0200 Subject: [PATCH 47/54] Fix throwing errors --- src/metadata.js | 8 +++++--- web/viewer.js | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/metadata.js b/src/metadata.js index ba87f7b84..56ac792ca 100644 --- a/src/metadata.js +++ b/src/metadata.js @@ -16,13 +16,15 @@ var Metadata = (function MetadataClosure() { parse: function() { var doc = this.metaDocument; var rdf = doc.documentElement; - if (rdf.tagName.toLowerCase() !== 'rdf:rdf') { // Wrapped in + + if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') { // Wrapped in rdf = rdf.firstChild; - while (rdf.nodeName && rdf.nodeName.toLowerCase() !== 'rdf:rdf') + while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf') rdf = rdf.nextSibling; } - if (rdf.nodeName.toLowerCase() !== 'rdf:rdf' || !rdf.hasChildNodes()) + var nodeName = (rdf) ? rdf.nodeName.toLowerCase() : null; + if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes()) return; var childNodes = rdf.childNodes, desc, namespace, entries, entry; diff --git a/web/viewer.js b/web/viewer.js index af34f6192..df3bf8e10 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -500,6 +500,7 @@ var PDFView = { this.parseScale(kDefaultScale, true); } + this.metadata = null; var metadata = pdf.catalog.metadata; var info = pdf.info; var pdfTitle; From 102469d20cd4a62f3f2a4b69cecccc657e2e6836 Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Wed, 28 Mar 2012 19:29:05 +0200 Subject: [PATCH 48/54] expose documentInfo in viewer --- web/viewer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/viewer.js b/web/viewer.js index df3bf8e10..c477ddb4f 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -502,7 +502,7 @@ var PDFView = { this.metadata = null; var metadata = pdf.catalog.metadata; - var info = pdf.info; + var info = this.documentInfo = pdf.info; var pdfTitle; if (metadata) { From 10fb0dc3833c9c48878dca712ed112479bea1afd Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Wed, 28 Mar 2012 20:07:37 +0200 Subject: [PATCH 49/54] Use strict and expose in build version --- src/metadata.js | 4 +++- web/viewer.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/metadata.js b/src/metadata.js index 56ac792ca..48bc902a9 100644 --- a/src/metadata.js +++ b/src/metadata.js @@ -1,4 +1,6 @@ -var Metadata = (function MetadataClosure() { +'use strict'; + +var Metadata = PDFJS.Metadata = (function MetadataClosure() { function Metadata(meta) { if (typeof meta === 'string') { var parser = new DOMParser(); diff --git a/web/viewer.js b/web/viewer.js index c477ddb4f..d3f0a329b 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -506,7 +506,7 @@ var PDFView = { var pdfTitle; if (metadata) { - this.metadata = metadata = new Metadata(metadata); + this.metadata = metadata = new PDFJS.Metadata(metadata); if (metadata.has('dc:title')) pdfTitle = metadata.get('dc:title'); From e9b52056125de88cb1670b3defa71ade92558f1c Mon Sep 17 00:00:00 2001 From: Saebekassebil Date: Wed, 28 Mar 2012 20:09:03 +0200 Subject: [PATCH 50/54] File header --- src/metadata.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/metadata.js b/src/metadata.js index 48bc902a9..7f3f24a86 100644 --- a/src/metadata.js +++ b/src/metadata.js @@ -1,3 +1,6 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + 'use strict'; var Metadata = PDFJS.Metadata = (function MetadataClosure() { From 89ceead8b004754364e706eaa1316f8d4468b0c7 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 28 Mar 2012 17:06:41 -0400 Subject: [PATCH 51/54] make bottest --- make.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/make.js b/make.js index 973cb445a..13768ebb5 100755 --- a/make.js +++ b/make.js @@ -357,13 +357,22 @@ target.chrome = function() { // target.test = function() { target.browsertest(); -// target.unittest(); + target.unittest(); +}; + +// +// make bottest +// (Special tests for the Github bot) +// +target.bottest = function() { + target.browsertest({noreftest: true}); + // target.unittest(); }; // // make browsertest // -target.browsertest = function() { +target.browsertest = function(options) { cd(ROOT_DIR); echo(); echo('### Running browser tests'); @@ -377,8 +386,10 @@ target.browsertest = function() { exit(1); } + var reftest = options.noreftest ? '' : '--reftest'; + cd('test'); - exec(PYTHON_BIN + ' -u test.py --reftest --browserManifestFile=' + PDF_BROWSERS + + exec(PYTHON_BIN + ' -u test.py '+reftest+' --browserManifestFile=' + PDF_BROWSERS + ' --manifestFile=' + PDF_TEST, {async: true}); }; From c0289497ec4c8112bd4d78aa18c4a6ba5b761e45 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 28 Mar 2012 18:03:31 -0400 Subject: [PATCH 52/54] bumping ShellJS --- external/shelljs/README.md | 9 ++++++-- external/shelljs/make.js | 2 +- external/shelljs/package.json | 35 +++++++++++++++++++++-------- external/shelljs/shell.js | 42 +++++++++++++++++++++-------------- 4 files changed, 59 insertions(+), 29 deletions(-) diff --git a/external/shelljs/README.md b/external/shelljs/README.md index 9f5386efe..af5f05127 100644 --- a/external/shelljs/README.md +++ b/external/shelljs/README.md @@ -138,7 +138,7 @@ Examples: ```javascript find('src', 'lib'); find(['src', 'lib']); // same as above -find('.').filter(function(file) { return file.match(/\.js$/); }) +find('.').filter(function(file) { return file.match(/\.js$/); }); ``` Returns array of all files (however deep) in the given paths. @@ -325,6 +325,10 @@ When in synchronous mode returns the object `{ code:..., output:... }`, containi `output` (stdout + stderr) and its exit `code`. Otherwise the `callback` gets the arguments `(code, output)`. +**Note:** For long-lived processes, it's best to run `exec()` asynchronously as +the current synchronous implementation uses a lot of CPU. This should be getting +fixed soon. + ## Non-Unix commands @@ -346,7 +350,8 @@ silent(true); silent(silentState); // restore old silent state ``` -Suppresses all output if `state = true`. Returns state if no arguments given. +Suppresses all command output if `state = true`, except for `echo()` calls. +Returns state if no arguments given. ## Deprecated diff --git a/external/shelljs/make.js b/external/shelljs/make.js index 9c92dadc8..c495735bf 100644 --- a/external/shelljs/make.js +++ b/external/shelljs/make.js @@ -23,7 +23,7 @@ setTimeout(function() { if (oldTarget.done && !force) return; oldTarget.done = true; - return oldTarget(arguments); + return oldTarget.apply(oldTarget, arguments); } })(t, target[t]); diff --git a/external/shelljs/package.json b/external/shelljs/package.json index 99c0f5e64..4d2830cb9 100644 --- a/external/shelljs/package.json +++ b/external/shelljs/package.json @@ -1,12 +1,29 @@ -{ "name": "shelljs" -, "version": "0.0.5pre2" -, "author": "Artur Adib " -, "description": "Portable Unix shell commands for Node.js" -, "keywords": ["unix", "shell", "makefile", "make", "jake", "synchronous"] -, "repository": "git://github.com/arturadib/shelljs" -, "homepage": "http://github.com/arturadib/shelljs" -, "main": "./shell.js" -, "scripts": { +{ + "name": "shelljs", + "version": "0.0.5pre4", + "author": "Artur Adib ", + "description": "Portable Unix shell commands for Node.js", + "keywords": [ + "unix", + "shell", + "makefile", + "make", + "jake", + "synchronous" + ], + "repository": { + "type": "git", + "url": "git://github.com/arturadib/shelljs.git" + }, + "homepage": "http://github.com/arturadib/shelljs", + "main": "./shell.js", + "scripts": { "test": "node scripts/run-tests" + }, + "dependencies": {}, + "devDependencies": {}, + "optionalDependencies": {}, + "engines": { + "node": "*" } } diff --git a/external/shelljs/shell.js b/external/shelljs/shell.js index 9c1181fb6..92c49c54d 100644 --- a/external/shelljs/shell.js +++ b/external/shelljs/shell.js @@ -777,7 +777,7 @@ exports.which = wrap('which', _which); //@ like `.to()`. function _echo(options) { var messages = [].slice.call(arguments, 1); - log.apply(this, messages); + console.log.apply(this, messages); return ShellString(messages.join(' ')); }; exports.echo = wrap('echo', _echo); @@ -809,6 +809,10 @@ exports.env = process.env; //@ When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's //@ `output` (stdout + stderr) and its exit `code`. Otherwise the `callback` gets the //@ arguments `(code, output)`. +//@ +//@ **Note:** For long-lived processes, it's best to run `exec()` asynchronously as +//@ the current synchronous implementation uses a lot of CPU. This should be getting +//@ fixed soon. function _exec(command, options, callback) { if (!command) error('must specify command'); @@ -868,7 +872,8 @@ exports.error = function() { //@ silent(silentState); // restore old silent state //@ ``` //@ -//@ Suppresses all output if `state = true`. Returns state if no arguments given. +//@ Suppresses all command output if `state = true`, except for `echo()` calls. +//@ Returns state if no arguments given. exports.silent = function(_state) { if (typeof _state !== 'boolean') return state.silent; @@ -1020,7 +1025,7 @@ function wrap(cmd, fn, options) { } catch (e) { if (!state.error) { // If state.error hasn't been set it's an error thrown by Node, not us - probably a bug... - console.log('maker.js: internal error'); + console.log('shell.js: internal error'); console.log(e.stack || e); process.exit(1); } @@ -1028,7 +1033,7 @@ function wrap(cmd, fn, options) { throw e; } - state.currentCmd = 'maker.js'; + state.currentCmd = 'shell.js'; return retValue; } } // wrap @@ -1151,8 +1156,7 @@ function rmdirSyncRecursive(dir, force) { try { result = fs.rmdirSync(dir); } catch(e) { - if (e.code === 'ENOTEMPTY') - error('directory not empty: ' + dir, true); + error('could not remove directory (code '+e.code+'): ' + dir, true); } return result; @@ -1234,8 +1238,11 @@ function tempDir() { // Wrapper around exec() to enable echoing output to console in real time function execAsync(cmd, opts, callback) { - var output = '', - silent = 'silent' in opts ? opts.silent : state.silent; + var output = ''; + + var options = extend({ + silent: state.silent + }, opts); var c = child.exec(cmd, {env: process.env}, function(err) { if (callback) @@ -1244,14 +1251,14 @@ function execAsync(cmd, opts, callback) { c.stdout.on('data', function(data) { output += data; - if (!silent) - write(data); + if (!options.silent) + process.stdout.write(data); }); c.stderr.on('data', function(data) { output += data; - if (!silent) - write(data); + if (!options.silent) + process.stdout.write(data); }); } @@ -1325,11 +1332,12 @@ function execSync(cmd, opts) { var stdout = fs.readFileSync(stdoutFile, 'utf8'); - _unlinkSync(scriptFile); - _unlinkSync(stdoutFile); - _unlinkSync(codeFile); - _unlinkSync(sleepFile); - + // No biggie if we can't erase the files now -- they're in a temp dir anyway + try { _unlinkSync(scriptFile); } catch(e) {}; + try { _unlinkSync(stdoutFile); } catch(e) {}; + try { _unlinkSync(codeFile); } catch(e) {}; + try { _unlinkSync(sleepFile); } catch(e) {}; + // True if successful, false if not var obj = { code: code, From 3740bed7d79374b2284c1d43836db334d450e603 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 29 Mar 2012 15:04:12 -0400 Subject: [PATCH 53/54] minor fixes --- make.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/make.js b/make.js index 13768ebb5..05ed2b39d 100755 --- a/make.js +++ b/make.js @@ -386,7 +386,7 @@ target.browsertest = function(options) { exit(1); } - var reftest = options.noreftest ? '' : '--reftest'; + var reftest = (options && options.noreftest) ? '' : '--reftest'; cd('test'); exec(PYTHON_BIN + ' -u test.py '+reftest+' --browserManifestFile=' + PDF_BROWSERS + @@ -427,6 +427,28 @@ target.makeref = function() { exit(1); } + cd('test'); + exec(PYTHON_BIN + ' -u test.py --masterMode --browserManifestFile=' + PDF_BROWSERS, + {async: true}); +}; + +// +// make botmakeref +// +target.botmakeref = function() { + cd(ROOT_DIR); + echo(); + echo('### Creating reference images'); + + var PDF_TEST = env['PDF_TEST'] || 'test_manifest.json', + PDF_BROWSERS = env['PDF_BROWSERS'] || 'resources/browser_manifests/browser_manifest.json'; + + if (!test('-f', 'test/' + PDF_BROWSERS)) { + echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.'); + echo('Try copying one of the examples in test/resources/browser_manifests/'); + exit(1); + } + cd('test'); exec(PYTHON_BIN + ' -u test.py --masterMode --noPrompts --browserManifestFile=' + PDF_BROWSERS, {async: true}); From fbc0ed45de479000464ee53ee775dd7dc5447e1d Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 29 Mar 2012 15:12:27 -0400 Subject: [PATCH 54/54] oops: remove makeref --- make.js | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/make.js b/make.js index 05ed2b39d..7f48e6d93 100755 --- a/make.js +++ b/make.js @@ -410,28 +410,6 @@ target.unittest = function() { exec('make', {async: true}); }; -// -// make makeref -// -target.makeref = function() { - cd(ROOT_DIR); - echo(); - echo('### Creating reference images'); - - var PDF_TEST = env['PDF_TEST'] || 'test_manifest.json', - PDF_BROWSERS = env['PDF_BROWSERS'] || 'resources/browser_manifests/browser_manifest.json'; - - if (!test('-f', 'test/' + PDF_BROWSERS)) { - echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.'); - echo('Try copying one of the examples in test/resources/browser_manifests/'); - exit(1); - } - - cd('test'); - exec(PYTHON_BIN + ' -u test.py --masterMode --browserManifestFile=' + PDF_BROWSERS, - {async: true}); -}; - // // make botmakeref //