From 14d5fa912f341ec2946ff20ffa7b4c04f645b920 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Thu, 24 Jul 2014 20:13:54 +0000 Subject: [PATCH] Patch from Shaun Kalley from bug #56023 - Allow XSSF event model to find + return comments, and use this for the event based .xlsx text extractor git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1613266 13f79535-47bb-0310-9956-ffa450edef68 --- .../eventusermodel/XSSFSheetXMLHandler.java | 159 +++++++++++++++++- .../XSSFEventBasedExcelExtractor.java | 36 +++- .../TestXSSFEventBasedExcelExtractor.java | 68 +++++++- test-data/spreadsheet/commentTest.xlsx | Bin 0 -> 36701 bytes 4 files changed, 246 insertions(+), 17 deletions(-) create mode 100644 test-data/spreadsheet/commentTest.xlsx diff --git a/src/ooxml/java/org/apache/poi/xssf/eventusermodel/XSSFSheetXMLHandler.java b/src/ooxml/java/org/apache/poi/xssf/eventusermodel/XSSFSheetXMLHandler.java index 0ebb40858a..06c9f09b9a 100644 --- a/src/ooxml/java/org/apache/poi/xssf/eventusermodel/XSSFSheetXMLHandler.java +++ b/src/ooxml/java/org/apache/poi/xssf/eventusermodel/XSSFSheetXMLHandler.java @@ -16,13 +16,22 @@ ==================================================================== */ package org.apache.poi.xssf.eventusermodel; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + import org.apache.poi.ss.usermodel.BuiltinFormats; import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; +import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.apache.poi.xssf.usermodel.XSSFComment; import org.apache.poi.xssf.usermodel.XSSFRichTextString; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComment; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; @@ -54,6 +63,15 @@ public class XSSFSheetXMLHandler extends DefaultHandler { */ private StylesTable stylesTable; + /** + * Table with cell comments + */ + private CommentsTable commentsTable; + + /** + * Read only access to the shared strings table, for looking + * up (most) string cell's contents + */ private ReadOnlySharedStringsTable sharedStringsTable; /** @@ -78,6 +96,7 @@ public class XSSFSheetXMLHandler extends DefaultHandler { private short formatIndex; private String formatString; private final DataFormatter formatter; + private int rowNum; private String cellRef; private boolean formulasNotResults; @@ -86,6 +105,8 @@ public class XSSFSheetXMLHandler extends DefaultHandler { private StringBuffer formula = new StringBuffer(); private StringBuffer headerFooter = new StringBuffer(); + private Queue commentCellRefs; + /** * Accepts objects needed while parsing. * @@ -94,17 +115,36 @@ public class XSSFSheetXMLHandler extends DefaultHandler { */ public XSSFSheetXMLHandler( StylesTable styles, + CommentsTable comments, ReadOnlySharedStringsTable strings, SheetContentsHandler sheetContentsHandler, DataFormatter dataFormatter, boolean formulasNotResults) { this.stylesTable = styles; + this.commentsTable = comments; this.sharedStringsTable = strings; this.output = sheetContentsHandler; this.formulasNotResults = formulasNotResults; this.nextDataType = xssfDataType.NUMBER; this.formatter = dataFormatter; + init(); + } + + /** + * Accepts objects needed while parsing. + * + * @param styles Table of styles + * @param strings Table of shared strings + */ + public XSSFSheetXMLHandler( + StylesTable styles, + ReadOnlySharedStringsTable strings, + SheetContentsHandler sheetContentsHandler, + DataFormatter dataFormatter, + boolean formulasNotResults) { + this(styles, null, strings, sheetContentsHandler, dataFormatter, formulasNotResults); } + /** * Accepts objects needed while parsing. * @@ -118,6 +158,16 @@ public class XSSFSheetXMLHandler extends DefaultHandler { boolean formulasNotResults) { this(styles, strings, sheetContentsHandler, new DataFormatter(), formulasNotResults); } + + private void init() { + if (commentsTable != null) { + commentCellRefs = new LinkedList(); + List commentList = commentsTable.getCTComments().getCommentList().getCommentList(); + for (CTComment comment : commentList) { + commentCellRefs.add(new CellReference(comment.getRef())); + } + } + } private boolean isTextTag(String name) { if("v".equals(name)) { @@ -190,7 +240,7 @@ public class XSSFSheetXMLHandler extends DefaultHandler { headerFooter.setLength(0); } else if("row".equals(name)) { - int rowNum = Integer.parseInt(attributes.getValue("r")) - 1; + rowNum = Integer.parseInt(attributes.getValue("r")) - 1; output.startRow(rowNum); } // c => cell @@ -304,14 +354,25 @@ public class XSSFSheetXMLHandler extends DefaultHandler { break; } + // Do we have a comment for this cell? + checkForEmptyCellComments(EmptyCellCommentsCheckType.CELL); + XSSFComment comment = commentsTable != null ? commentsTable.findCellComment(cellRef) : null; + // Output - output.cell(cellRef, thisStr); + output.cell(cellRef, thisStr, comment); } else if ("f".equals(name)) { fIsOpen = false; } else if ("is".equals(name)) { isIsOpen = false; } else if ("row".equals(name)) { - output.endRow(); + // Handle any "missing" cells which had comments attached + checkForEmptyCellComments(EmptyCellCommentsCheckType.END_OF_ROW); + + // Finish up the row + output.endRow(rowNum); + } else if ("sheetData".equals(name)) { + // Handle any "missing" cells which had comments attached + checkForEmptyCellComments(EmptyCellCommentsCheckType.END_OF_SHEET_DATA); } else if("oddHeader".equals(name) || "evenHeader".equals(name) || "firstHeader".equals(name)) { @@ -342,6 +403,90 @@ public class XSSFSheetXMLHandler extends DefaultHandler { headerFooter.append(ch, start, length); } } + + /** + * Do a check for, and output, comments in otherwise empty cells. + */ + private void checkForEmptyCellComments(EmptyCellCommentsCheckType type) { + if (commentCellRefs != null && !commentCellRefs.isEmpty()) { + // If we've reached the end of the sheet data, output any + // comments we haven't yet already handled + if (type == EmptyCellCommentsCheckType.END_OF_SHEET_DATA) { + while (!commentCellRefs.isEmpty()) { + outputEmptyCellComment(commentCellRefs.remove()); + } + return; + } + + // At the end of a row, handle any comments for "missing" rows before us + if (this.cellRef == null) { + if (type == EmptyCellCommentsCheckType.END_OF_ROW) { + while (!commentCellRefs.isEmpty()) { + if (commentCellRefs.peek().getRow() == rowNum) { + outputEmptyCellComment(commentCellRefs.remove()); + } else { + return; + } + } + return; + } else { + throw new IllegalStateException("Cell ref should be null only if there are only empty cells in the row; rowNum: " + rowNum); + } + } + + CellReference nextCommentCellRef; + do { + CellReference cellRef = new CellReference(this.cellRef); + CellReference peekCellRef = commentCellRefs.peek(); + if (type == EmptyCellCommentsCheckType.CELL && cellRef.equals(peekCellRef)) { + // remove the comment cell ref from the list if we're about to handle it alongside the cell content + commentCellRefs.remove(); + return; + } else { + // fill in any gaps if there are empty cells with comment mixed in with non-empty cells + int comparison = cellRefComparator.compare(peekCellRef, cellRef); + if (comparison > 0 && type == EmptyCellCommentsCheckType.END_OF_ROW && peekCellRef.getRow() <= rowNum) { + nextCommentCellRef = commentCellRefs.remove(); + outputEmptyCellComment(nextCommentCellRef); + } else if (comparison < 0 && type == EmptyCellCommentsCheckType.CELL && peekCellRef.getRow() <= rowNum) { + nextCommentCellRef = commentCellRefs.remove(); + outputEmptyCellComment(nextCommentCellRef); + } else { + nextCommentCellRef = null; + } + } + } while (nextCommentCellRef != null && !commentCellRefs.isEmpty()); + } + } + + + /** + * Output an empty-cell comment. + */ + private void outputEmptyCellComment(CellReference cellRef) { + String cellRefString = cellRef.formatAsString(); + XSSFComment comment = commentsTable.findCellComment(cellRefString); + output.emptyCellComment(cellRefString, comment); + } + + private enum EmptyCellCommentsCheckType { + CELL, + END_OF_ROW, + END_OF_SHEET_DATA + } + private static final Comparator cellRefComparator = new Comparator() { + @Override + public int compare(CellReference o1, CellReference o2) { + int result = compare(o1.getRow(), o2.getRow()); + if (result == 0) { + result = compare(o1.getCol(), o2.getCol()); + } + return result; + } + public int compare(int x, int y) { + return (x < y) ? -1 : ((x == y) ? 0 : 1); + } + }; /** * You need to implement this to handle the results @@ -351,9 +496,11 @@ public class XSSFSheetXMLHandler extends DefaultHandler { /** A row with the (zero based) row number has started */ public void startRow(int rowNum); /** A row with the (zero based) row number has ended */ - public void endRow(); - /** A cell, with the given formatted value, was encountered */ - public void cell(String cellReference, String formattedValue); + public void endRow(int rowNum); + /** A cell, with the given formatted value, and possibly a comment, was encountered */ + public void cell(String cellReference, String formattedValue, XSSFComment comment); + /** A comment for an otherwise-empty cell was encountered */ + public void emptyCellComment(String cellReference, XSSFComment comment); /** A header or footer has been encountered */ public void headerFooter(String text, boolean isHeader, String tagName); } diff --git a/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFEventBasedExcelExtractor.java b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFEventBasedExcelExtractor.java index 6929242f3f..c598ed2e3d 100644 --- a/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFEventBasedExcelExtractor.java +++ b/src/ooxml/java/org/apache/poi/xssf/extractor/XSSFEventBasedExcelExtractor.java @@ -39,7 +39,9 @@ import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable; import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler; import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler.SheetContentsHandler; +import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.xssf.model.StylesTable; +import org.apache.poi.xssf.usermodel.XSSFComment; import org.apache.poi.xssf.usermodel.XSSFShape; import org.apache.poi.xssf.usermodel.XSSFSimpleShape; import org.apache.xmlbeans.XmlException; @@ -60,6 +62,7 @@ public class XSSFEventBasedExcelExtractor extends POIXMLTextExtractor private Locale locale; private boolean includeTextBoxes = true; private boolean includeSheetNames = true; + private boolean includeCellComments = false; private boolean includeHeadersFooters = true; private boolean formulasNotResults = false; @@ -112,11 +115,10 @@ public class XSSFEventBasedExcelExtractor extends POIXMLTextExtractor } /** - * Would control the inclusion of cell comments from the document, - * if we supported it + * Should cell comments be included? Default is false */ public void setIncludeCellComments(boolean includeCellComments) { - throw new IllegalStateException("Comment extraction not supported in streaming mode, please use XSSFExcelExtractor"); + this.includeCellComments = includeCellComments; } public void setLocale(Locale locale) { @@ -159,6 +161,7 @@ public class XSSFEventBasedExcelExtractor extends POIXMLTextExtractor public void processSheet( SheetContentsHandler sheetContentsExtractor, StylesTable styles, + CommentsTable comments, ReadOnlySharedStringsTable strings, InputStream sheetInputStream) throws IOException, SAXException { @@ -176,7 +179,7 @@ public class XSSFEventBasedExcelExtractor extends POIXMLTextExtractor SAXParser saxParser = saxFactory.newSAXParser(); XMLReader sheetParser = saxParser.getXMLReader(); ContentHandler handler = new XSSFSheetXMLHandler( - styles, strings, sheetContentsExtractor, formatter, formulasNotResults); + styles, comments, strings, sheetContentsExtractor, formatter, formulasNotResults); sheetParser.setContentHandler(handler); sheetParser.parse(sheetSource); } catch(ParserConfigurationException e) { @@ -203,7 +206,8 @@ public class XSSFEventBasedExcelExtractor extends POIXMLTextExtractor text.append(iter.getSheetName()); text.append('\n'); } - processSheet(sheetExtractor, styles, strings, stream); + CommentsTable comments = includeCellComments ? iter.getSheetComments() : null; + processSheet(sheetExtractor, styles, comments, strings, stream); if (includeHeadersFooters) { sheetExtractor.appendHeaderText(text); } @@ -268,17 +272,32 @@ public class XSSFEventBasedExcelExtractor extends POIXMLTextExtractor firstCellOfRow = true; } - public void endRow() { + public void endRow(int rowNum) { output.append('\n'); } - public void cell(String cellRef, String formattedValue) { + public void cell(String cellRef, String formattedValue, XSSFComment comment) { if(firstCellOfRow) { firstCellOfRow = false; } else { output.append('\t'); } - output.append(formattedValue); + if (formattedValue != null) { + output.append(formattedValue); + } + if (includeCellComments && comment != null) { + String commentText = comment.getString().getString().replace('\n', ' '); + output.append(formattedValue != null ? " Comment by " : "Comment by "); + if (commentText.startsWith(comment.getAuthor() + ": ")) { + output.append(commentText); + } else { + output.append(comment.getAuthor()).append(": ").append(commentText); + } + } + } + + public void emptyCellComment(String cellRef, XSSFComment comment) { + cell(cellRef, null, comment); } public void headerFooter(String text, boolean isHeader, String tagName) { @@ -287,7 +306,6 @@ public class XSSFEventBasedExcelExtractor extends POIXMLTextExtractor } } - /** * Append the text for the named header or footer if found. */ diff --git a/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFEventBasedExcelExtractor.java b/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFEventBasedExcelExtractor.java index 98aeb627f2..80c1f116b5 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFEventBasedExcelExtractor.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFEventBasedExcelExtractor.java @@ -20,13 +20,13 @@ package org.apache.poi.xssf.extractor; import java.util.regex.Matcher; import java.util.regex.Pattern; -import junit.framework.TestCase; - import org.apache.poi.POITextExtractor; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.extractor.ExcelExtractor; import org.apache.poi.xssf.XSSFTestDataSamples; +import junit.framework.TestCase; + /** * Tests for {@link XSSFEventBasedExcelExtractor} */ @@ -240,4 +240,68 @@ public class TestXSSFEventBasedExcelExtractor extends TestCase { fixture.setIncludeHeadersFooters(false); assertEquals(expectedOutputWithoutHeadersAndFooters, fixture.getText()); } + + /** + * Test that XSSFEventBasedExcelExtractor outputs comments when specified. + * The output will contain two improvements over the output from + * XSSFExcelExtractor in that (1) comments from empty cells will be + * outputted, and (2) the author will not be outputted twice. + *

+ * This test will need to be modified if these improvements are ported to + * XSSFExcelExtractor. + */ + public void testCommentsComparedToNonEventBasedExtractor() + throws Exception { + + String expectedOutputWithoutComments = + "Sheet1\n" + + "\n" + + "abc\n" + + "\n" + + "123\n" + + "\n" + + "\n" + + "\n"; + + String nonEventBasedExtractorOutputWithComments = + "Sheet1\n" + + "\n" + + "abc Comment by Shaun Kalley: Shaun Kalley: Comment A2\n" + + "\n" + + "123 Comment by Shaun Kalley: Shaun Kalley: Comment B4\n" + + "\n" + + "\n" + + "\n"; + + String eventBasedExtractorOutputWithComments = + "Sheet1\n" + + "Comment by Shaun Kalley: Comment A1\tComment by Shaun Kalley: Comment B1\n" + + "abc Comment by Shaun Kalley: Comment A2\tComment by Shaun Kalley: Comment B2\n" + + "Comment by Shaun Kalley: Comment A3\tComment by Shaun Kalley: Comment B3\n" + + "Comment by Shaun Kalley: Comment A4\t123 Comment by Shaun Kalley: Comment B4\n" + + "Comment by Shaun Kalley: Comment A5\tComment by Shaun Kalley: Comment B5\n" + + "Comment by Shaun Kalley: Comment A7\tComment by Shaun Kalley: Comment B7\n" + + "Comment by Shaun Kalley: Comment A8\tComment by Shaun Kalley: Comment B8\n"; + + XSSFExcelExtractor extractor = new XSSFExcelExtractor( + XSSFTestDataSamples.openSampleWorkbook("commentTest.xlsx")); + try { + assertEquals(expectedOutputWithoutComments, extractor.getText()); + extractor.setIncludeCellComments(true); + assertEquals(nonEventBasedExtractorOutputWithComments, extractor.getText()); + } finally { + extractor.close(); + } + + XSSFEventBasedExcelExtractor fixture = + new XSSFEventBasedExcelExtractor( + XSSFTestDataSamples.openSamplePackage("commentTest.xlsx")); + try { + assertEquals(expectedOutputWithoutComments, fixture.getText()); + fixture.setIncludeCellComments(true); + assertEquals(eventBasedExtractorOutputWithComments, fixture.getText()); + } finally { + fixture.close(); + } + } } diff --git a/test-data/spreadsheet/commentTest.xlsx b/test-data/spreadsheet/commentTest.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..10e7837d64fa152cc062d797b5e7d49ad545d880 GIT binary patch literal 36701 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

1}H?%>wo;XXqv-YgDB1Ah6 z#ZSx4%wNF*xy%MwptUXn{=>(7_{T-K2Q2dRQLqIT!2day4FJr4M!P|9a2N$&Q~Hbr zkZ#}vW;%qg->@tLzcnoO#O;&;U9E4%tlILKL3OACD*4JKJz@QzYOXdSzOb{rtUj*9 zpY{4taI9;U*<3HxdHFje4*Eq^1=^JyDp{XiY}}W+>&H8oaLqJnu{KlLcqn>Y+lk+3 zDs^$*D!$c{nu{O|wq?$M|0XStRYDE_(vr)q=#J$#IvTXDj0AEnk2};QhV$sg5}f&d zxEv^*yv7h6o3FKy%W`8bBje*&hD0ssnIn#NvX^C8+gn4}h8ryqjMt~#JXFU-)m?O> zcWB<4*$>cGec_^^{`N{8HwxOA2>pPI>2nRom)Uw7oUPuw5Id-j5^1*R;0GhHea&a#NfeQr5h ztgH9sVTTGY|9pDUU2(!^9-Ebawzx`5%!mJ`Wdi?V;wzXd9HJSc zowF{R#T^ZdLiyM~kEK3-tZ3Kodehck`vS1}zt*kGW-|--52RvcXn8g|18p;Ocsi9s z>d|UE-^3h-;eCb{(aRD=?!=Y|J2zj}O;*fw$BnLXUS}JsjMqxemb zR>7X-B5_N2Hl-}1QwvEWLD2@26f!w^Kl@P)!~F);%8O2(voof*3up3QYZ9kkW!F(y z%zsuNsf1^UkE=y6Acya^46ZeuR*F%6P zifpmAm?^Zl1e5DFzlvU6TtUSpmY~PmZ%p`lOuH;IXd>ZisIJOm26Upqm?)y$#HGHa zGaC=w5l}*MEpuSt=VhBPJhyyyifV^K}f}EcQZ9=x2**qR(Tsew@gGv z37bFcl4{NkUY-gOLsu_Bx>w9y#zTrjUyij-TE$%iktO<;kz z@$M96WD?BhG;CVwl$alm-Uj~ew^*YdB1u00g&!OBpt`|XKl)XUZM9Gm0Q9oI!0F8! zc*4b*M+M$HOHh70b6zqx+eh1j>uv}d34MS5@qAB^)AhMD4{f?+(pGlT$fQ~ z(eB@)zMrUCjLr8ZNT0ax462RtSMFb^o0~1jeDgj*$*h}R)bZ{?MYxpiNt2U7*}9_8 zLG3E7qTeg;ne?)}^E~RuBVRPsn>1C02Sj3f?Z)aJNr% zED27zlUQ@`XnoMCjOiT9jVN*!EikXFjoc-u48@^xkQ zkjul`3NFW%J)1=)5&HP1r93@$61+EY&yfIre`f)IS08xT8Bgvg&P?10$E6=n6&-~s zS;TSYHNOpHfA)PQTtR_?+QFZN_kk(}fSV5VqjeT7a4mv_ktAPs!y2y!1o$8r&)v@` ztt4}zoHbSF%y1u3hkl!CtmLs!+Yzy}74Q!y|jxei*;jdF1;c+&um{tPfrPAi?9t69L{ zoY{~r0Jm{s{!TawN8*DH_(TimPwknBf{0EcPDxG&T56fQc4Op-ws&gEd(JUVkk>A=&IYt%ER40KB5247;-O3fD=JDKP9QHnnDrd=`5lZzirM2TkXu^@f3)d zrR%Kml=yf`$9H@o8O0%`m6!4Ly@r=6TS52-hijP<-E=10aq3&h$sg(y9a1X zBlZ`$+HJYV*FtV^A-)e~801exqiCA^da{w<(4c*ZM|Eo~&-exJEi(?P!MGOdg~P zX6IiwQy;#Z-{0ABTNx98x`wf~dmhkGhPIBCLd9G|*cenU<*)Kdb=-`Up^)PWWR zipjjRYW~;hVy$BGA~i`j5GUn$x03Hz6$iihqdl1;e*B%q{hh_)^uWZGN3upu1qz+E z3_4G*-Z1_+`Lv|m>{W5U(ibh@?hwwyPQVWf{9)|>*3kc@Te0uKENssToZb_5z;mXv zZg4f4o{Hl1!bAj+b3ZM20#qQ>h+v`ewGxWB(g(TDMa32_wqpi_V|g0+O>zmat?hC`y6{A;;2P7T-ld5zs1N_!{5< zn5X82!DU#H5}0jidz?CP1E-%*sV5{-a;FrD@I7TPxCO+vtT_#X%dMmgERYWFpj0=4 z=Nj@F@!(-m{sLx1i=cG@Xy!PbGUq!fmX*Zto#Zq#pqUXnVaSPgDa9h>{Uvxp$ z4GYi&@hqK$e3PDu!U8qBuJ{?{u;3uMJf-rr0qObmfcn_Sd^(1v>*?VOgI^4{zBXH> zFIJm2$I+*dAc`J#B3AVa`26Tk=f7#>v*t-@B@W*S(G@hDAadS~rGS0-7MU$BV&`_Ui~JAbz) zwdDuz{cJn}0{Q&k4vKar-;gyxKLK}g@sUNLP&!nfar$Og{k0~fi1}!c(6WqlXA2Mn zFX}`*4l+OtXm&82M@>YzmG2gOWW`URw{AxqiXd@^eJ1&=WObl%21*t9 zU#^wsqTa!oGi;F0(^e&eyl34Rn4{XHs8*$tUTGH)iWEIA(DsV2DKeOrHR|+kHe=jL z-3SpyMn*yzJJOh#+dfHlc!8$}t1lQ5Q0zXg?qp=CU+F^Zo-b9a$x2`8e@%N29bdBi zZLoM4mVT*-ekhA|=UPUQcG1kAt^ewr9LwL2HstTU2$G15$X@EI9gmge7|DZlW^o%q z(Dp_FYV`v{DYw}=$hVX)(-^+)67)}F@A`%_{%3>wH@f(u>n zkqY{Rfi}!#aS!;~c&P|o+sKH}59_Gc)vAm1wU6UMc2q+heK);}!Ifg0Jo9s1Pm4bF z$xof100PaE%w>TidS5U<=EMSD6CV6`RN~2e$rY-fO#;1lXwEjW!=Kt_yr+q4PMn5fwYC6PSIY;U79`DAH>51uck!HtgRY{}O1f>pt-{Y@jB+=P z$^=pvWpQcreS&iW-;OT#hpL@JRlR{oUbBN3x9K!J%^-2R-i8O5G)lMADX0g9=F{qP%Wt6Sqv^fW_EMj97zqz`{w zA5m8UlO4B+fRl?_kwtMe&`7-hrb}z{S;nib2quO#pYZ2xG2c?y`t7$3>f- zUK@uxA;s03hh({7COXKhYQ&sL+fhng#q`Z0j?T9YZq}m(9rLuGpV_~q=<37OCJ-8+ z6n~bo`rh8nf-(HVM6#NqiMHsQnj*zop1URDcNO{d9tBoz6_vS{B{mnINXKEAmazay zl?LgO27m9GWtE0(>(UzxR~o_x+91;Q6|Hs&is!1QqUVj#xFSE0jCTiV{Q|OVWLI17PTYFueKi&Rl?%iC>ql_Dqc@F(p1#D;xFyn#eoY-;kjme zoy)dBP~>v_q(M841wpd}On!;1Lm=+-8ZsNaqyX+EEP+Rm+XmW5rS=v|*rtw4CXG?` z7-G0q1Yss`P%|MaM>c4cA0#mzB<iNbZW52?2Y4I$!hJ7zBXO51pyh!k~c-OBNPke;Jf!8S_b z@n~*j!8Whbv_ZKxRf&9hfpQnQTd>?QU&wgNdAQRFQ74#1$U@YYJQ&bi>f>9`3qHkt zFP-n%ZKp;x3gx$6D=M^@b}4ZvF8|!rTyDjepiZ;PlQg!Nc^ilJx&+nUsnw@2S%S~h>*=IWhRh*I^Vl_gV6%R5s>|z2bPKjaKJs{qH26{C$Fp~{`Im{89!OcBRd(gpv{3@5=j6cy?=|oa z!=sA5({KR_aBrSn4if_2<@h@c34!Pk_WJi|m*qZxN8xx6*ii}i-j0x}W4CO~$qak}-GY7v$j*QVsB-=bho=Ikybs9UPGNz; zxw;c90B$@+=RmgK!uJl{umBwN#HC{34^2@GWB_;sDJuouGNBaJB!j_KP!sXZ%oR_T zEvO9HuTRH6-hwihI~BE|T(lK78NW{pyVTcQ5Aac(2pwNBZrpWqbECREhJ+a#zEBiS zCiLvI&Q+vMAJ)%wM4S}8IY=4NU>bK)u5j0Xxw1@Ku)7Q^imJXG#({7R@p{tbc}NQ8 z2JW|$t$FL;nHEuIo@gHH!!C)gMn*^(g-{eYUrf*0)b`8M5OW^S)U^FQ=j+fv3i2Q6 z0MZpg3nM^uw_98aGLHFR&IBe%kIEZemKw;Sn7HbO*&;rU8ltiq^5yNy(Gf0=vnGYP zujKQczMgC^KfZSDip%@E6&2SwQ!_}y+7>_ih39I7c35*om(#nXh;-=2UePW!;ZfI# z6iZMqo!+k7UB&2hnCkEq7Lhy9&VDVtonZOSgQqn9)#iAsZJ?j2o-4kQMp;WB-*pPu ztV(-Vei5(k2Cq;;p2mwSTMKU0SRlK>;}}fZvA`uWt_RG2EV)52Y-V}a2FyiSQ5YN*q9+{?Rytd`nDpf)U<0k%vg zPnB2>b4c6&D@*yW6^yTZ-{DK|X{y^H^QlSa$>`~4z=sL1v4)8d`BYqgF`i^CCk$#C zD!S=;6R+w#^kU$kF|g76-?!VnmY7uka%962MfpI32u!X6;7%7-mU8O?aH^DLfR!PF z7Z^db2(ri!?X&1+po{1X-mPRt6EL(Yf((uUS$?jT`33Gw{I}NoM^caLI;BzBi+6}UbG-yPt`#u zm?#(3jJR1Rm>5AtmL$yyuE;u0-?4gH(z8G>u803XM&y*7j1WA2Y{-KzHlJ-)oh3Bq zBg5fqX2UH=3l}WV^)n30x3zq~wzhy4INRXz1>^G-gvAk!uLp)^8l3;@!MXk-vhswe zUi_438;+g>yi=K|kUglb#PdVG4=G_f9l|wm%eRvwoRx@hrK?{StI861->eW=%A$G* z4E|SoWJX583{J!67{JxkK?}zHfusyqTNa(iC3G|AkZ(jBccX-jyp^jSaY{h=$9Mrq zAn;b|smKjR2MjCIB`{41Py|l`g29sjhO;X+|0_yaX=FxNkf^$pr};8+-pb-fsogPe zn~LtHuJ(PF(;vjkLikDou+$D98r3dyrBIfbXG53+jtp@4v z&K4>se*<&cVTYPgx2+(mg9GYshBt<$ZybgYH#9ir#FjZuh#QDJeV-6Wo5I56f~cf2 z-^gZYyp8m4?}k%ZpOZ|}&!B?8Ca1`6Y1}b&i z;8;0`<)pGk4j-m^2d#$~B;ClygL)zJ+S5}K3xjSWQ)3&I%5Mi0n=YU$jZjZAPdbxZ zB1Ng1s^8QMgImigEFA8$KibT3>Q(< z#a}EaqqJY>ad&yeS9@j)I~6g&Rnb!)P9ZBH%xI-9b0U-y&8oz_U?ip0yG^!b;y%_c zn|BuCJ!{C>Vg!%Xw-DxDUMui=($}=ub%TyUMoqeiyvQu30?At~H77FQHmXfWqUJBu((V{&U= zD7lk8@D_mm8YBfZ7<9vaSG;`Nx-G9x=VqR zv3ZG44&3!5{@9vCT4%JfVDxmDv3x&&E3rF*Ym?caa$nPKzs1I2W5G}-narbCz-~K< z<3hdpXzDb#hOk!)U*gz48h%W&I>oyL-rdOJm!Y zkV$*YCHLVax}8>D5-{uF7o}R|>MAdq!AsAfK0$X*%4M&^*p`_Jj;$T9GZTV8XqGuk zkqcU2R?vPzUgJTs!Nd&0k&SZjm*~*WAU&Vut4$gUx{1bPOrwTXpKV?@RjYb2=8Lp@ zT6{p5wnO_pY12@olq6yOo>M7Y1iXeSe(PG9^t#Bs0(~NK3v$W(nl4|n z*_OL=N`Op!C^2)T70uNtxj=)w7W^TNfNE#}7Ri{rT;`HQ`CwjJy>f4Jv_S{D$ zp%D-g`scip0_9qjQq3bxh8n2?YhwGU2i9y0=-m$ezN=gD{Y-STJ2$28Dn;}OEq=q7 z(G**^4;B(Y*x~y<6>Y3 z@936_NGFv;Si+KyqoA@%t6(VV1UKh42QR>f_6V>*a?6jqm18Kmy|3`dACFJ+ir_4;8%96kLJJ_cNdBgLfit?n5;(Ke7?0R{uX>I*21;O7HO3+GTgmv)4L7 zRhM{{_3A)&f^m<^J6_NIf9-u|KvPS*ZV(lbCeow^ML}tiCP<5-Ql$veg{XjZkS3u9 zrFQ`VrHX(E2vMXcEfSi5NCyESB#1PTgc=}(JMR6RZ}07Q&b{}Xv(No?14&q6&8(Ts zthYSxGxO%kG<)(*2+>y*+MJ3`kiBu*JprlZo^un>eSmrqWho}0Wg%kN1-W;;IuttEwEJTni)tN5i{?J!_RpEFkd?3%ES%(oBUJuAvnxlD zUR_cAieI9pSCv&xW*!MF{ieAbBCm8#>$3)s1Bd=5u}VX-ywn&9bkiLa2Qp?xh?rKt z?}o&T=-%)6uW(@ zXnd`Q#ZjKeXEv#*2|DItpWbp-OYUW>Z?jM{o3fpGzM3Y4uP7u?Cl9Gl-~t9SPfeJr zG`Xk^!CEx|OfDm?Nhon(0z_-ax2tMPEM{pUUwp z=FTduXlaOlF6C4*s<(`QrtBNQY&rjQrT<&U(9;yb-AdJi)L!_pz;nX9SKzEAnQaa zG#{sa>`+*yi~gvfBUALm&M%Ek5qs;*R0+ZPR>{ocFSldz8QbYTl?I$IHu>r?8#WCs z#o)zLsNwD$s3}fx8+BQ%vvdQrsslb}LNcY&IN~T(Kgl2e+hfKCFTyEwq_-8Kg(o+h zVq5DL`5dvx6KdhmyC8qN3xq9K=Efa2xHjcSM)n=DJv%yT+2iXa{Mi+sW`EgbVKAP@ zSWG`N`OzuEz}Jsvx(>b5Nn_PByrEX zBc^wYqh2hWF?}Rv&_%TV)M?aaTC6ASS)UvMW#OqRk~mqczN$LjqR;RahjpRmG9sgs zjqzW8JU0*hbmpcg;gv$iaF^7&lq~UaiE#x*55^?wGaJ^@1{<4DbbUKox^wPb@Et3e zB}G*X$NHx-2K{2Ed(|G6H2j6ryBVxZ>&+L!_8_J4LR%k%HRcmTNerhG=#FD;NIo+O z-D38mPAxW01@CU1zKt0rKFXes#5UEP)|gChH8gbV`ns~6fJ`teJnWRUbYR5QcJop8 zx_gnG*D7pd8ED;bD{3oMoCptpN;be9!L?gMNaz?@KbQ4!+d;Il5kAtfvgbe(e$h0= zyxPkB>g!?jNNa8UO99sEOg$^%O|#d7KfXV$C)ekSMmXzLnMQ|0ZA{|NTW8J~yjT(~ z&)-A{dFW+XmJE2t@#vzpt9~m(PLXE@sve2h@%+%z#>VmDccFZS4^p0evVYeq#uII}R8u>dE zc8==@mvnWHEmZ}Nx1xw9P%zW?H!20%5{usF#tWQ8-B(p4o8!fL4s_=`noJ6jLvNAf zrz2yi2P_tIAT_Use9mFcHHr}F;;mI=46RhD4&D>zydaULHEq{d4k!O)AW1zIMcv4c zdQnm=e^@#%v?$J_eCAj|w&I%m>31i;t=HRzE?%n~3mcEMIOThC_QRM?x01hKe!DWj zhz^vKg9?$M+C)SM^_Uath_cReWGFs3mc(G(S5((PRH=s__14ufi~+EE#!y40e|prj z0hp}H?Y?J&-euAOnKt=24KB*nIL_VcZ|@Z3i4K*jg`YQSxt`DcqNHbGHSUJYENU^w^St$8#WvOh<~eAnJvJ^$x)MtIPy3 ztPGQUg8N||LN)`|LLl95YM14D;$;#n8ZDQJ=3Oh-ElzLtEF^qzkq8o26n{P1env#cAZuV+3o zG<{3R6AO4#H{zE52KVL>TSHay-$4T=vKMc(U}{))Y&iI@e|)M zsRCpC_)Uo02BI_3m6^B`;+a(uU)n`8Fxa=0HwpFKy1xZ%r~O279l@Q=8B=Nfr0g>} z(JZtX!>cOhkYO|4?5uqD^L&oNuz1;txb7EfY8v|;N)E$1A+!(l*U$&&vC2}em{-!7 zC_EGGj0b#KA$ECbVGlCzzP;+7Tad#*QhrE=V_&1Nas}@QIgs*V*=+{V03RX<8dRXJJu0u?GLu6ES)+dJ4xP zmOpW!{H1K2OX-=195<1IAs44sFF=rf7wp&5#G2@)+Z-LVCmD03FDzVv+=Ds+$SgMszPyG4K-quw2$_%$ z77!Li1gk6pv`$$CUq+`vT5d^<>~o+hktB23Icp}*R1&RgZc~r(bX_)vNmdq;IA3!# zeC-_3b@vqXvp9YH`q#qyScBHv9+eJ8MKNvaaZ<)N-`QCiY~*wdRwq9)){?yUF3Tm; zQD|O<8AiA<5-BZk^y8Z>H~B~;f-7?xi4toWO1+_KgitX%kg=^0f3(Ol+y!`x^ zO2(f7?0#mMH6B}1>D;+adfjADsK=INhe&&r^_qiy(81^$nuESm#^=|)6hF#;#^C=+bnx+8x>9(fJ zWznk);t2FkV{TP1#>g>q2>m={ep~R-R~gG%iDm=OgWNu?YBx4kCImzUq#?3A)ty-* z>*DOSbBD!BQo=3dO0foEPv03^C^!vh2PSK0J(}j0&8i*TE_PoIDCsByD-vABMhb_@ zf9U4}#7+ZlSEjalsV@_YVqJ4JZ3PPJ+x=$_=^(iI5^6An_Tj(O`lh)=!}-~wVnst@ zBw86(j$1J5#BPr{+PocbS$>{_&l5?zLyEV^!O<&RD>NxHZ)a0dlZuD>)FwqX7|Io^ z^Q=@A6eODlk7f4<&R4(8lABm;>?pdow3&5W*9H-Qbj9J=x%>E(f!I* zfm^!0@h-0Hi_^rd!0V!>CDTT1maG?Rezb{=4r7PIHQx!(@U~Vhvn@SEJ9ODg7{>Qs z3l1n2333fk(WBe&*{!6~2lpZ>S(XG?L#dE9tG6wYzn7E8mG&58L7FKY90}Sz9o)mRqzt~c>V_7mA zCdnI(11!4zEhtWt4QDP2W2r~wX z*@ILw{Nrp-Xtn~>FDOW(ST@H!zkd1+ka**?h9dwsPC+GY0iSyRy9^GJ$dUn%!z19V zT?n*4=6FpXEVcaf^cAItV>h$OpNL{mrNC1f0*UP0D@gQamc}lsY!6a&GPnZC`Rxl* z3@iBM!Xe^t^h3KuA?m5>iD0+J{1+;I=;vn@(N&hHo)+llHvDJM>;LXRd6_WNS{Eou z4l7j_Z*QUsLg)>W^`3aV=dIBCP6x2oClo2N*w3he&s3195s@nr=EJXn9XqwOKGPcJ zsqf?K^;^*3{LI%h6Z?BA=Fv}C!W$1bbLE`bEw8sC%C`glOCYUJ-o>YZuB4NFa~800 zN$(z-&qy{xPb5W%Say@d99n{Ns#&3^5%p?3N*2dZ@#1R@Hu@y>*b?*kbI-8YbpzTp zu*{RC8OE|0%v9eO$cEoNx8Tm&Z7)7N%qrC342uZV5Pv8&D^>aG(s3v*nztkdvW6pH zQU?16TwsEMaIFn!27u-miAB2*Sq$YsBIPuIW5Q&xxoZ$WG$C@riNLoy(sLNqORED+ zIWvIB0d()z8fM&-h4A;MgM?}F92X5}gTJ!|qblFZ!?8AFx~V+VT0vGlb5KCWuyMi zs@D0s!fTF=)eVUXBlX-<81_jsCxKH$bY#`ek|gfhn}PE6AjwWDp>x2r##z^Jc+&E* zEpn-f;f@1LkghA|oY;wnx+vy;LYD5KPwXB(XWBf&p68i(7^a^0Qq$`}c9}H{s3!H- z)fu|inW6%$?RZgtfs|`((c*_ZSR3fBWuLL$#NCEpMhyY+dow3~4l9y3DKdsVkD= zt?e0~J4-X9!^$iiwb`^~Vol!|#@)^cI{IBiODP~zX=%uH;CY>IP)GWE>wO8AM3M5% z^vC;`E2Zc29p^072iJCCT7%xt#UQvMgrmQ3Vv&`3_061io!8Y#JYr_4rrSfnMR?kY z#>MM8byHW4X636`Zsv=oB&RJHJ!%xWyP>mXJv`ob?|bsv>R?$OhMP*rO^y|~Gn8xuEr#I>7jlL_}+wkq68>Mi5S zxUuEu$~3R%R%Y2-c*d2Sd{!{vUzw~^p*_B@(Dm6+YFehxcVB_q+puqN>^syj9niU` z6+hJDE$Ak7lX~$R>>1UDa(WM92BmYe~LE44Wfw*98Vu1@HM51G%R|X--<7r2vLv&&Ncvut=mV?;eDmI>a_P ze~wEAIz~G!!!2B+;o45%toGF6OTnE-uhrb2Aj85keKAa56Ly!=@VPxflSS>fQIleO zkdXz<_Kn0L+GutHgCM;Sc3fp(YH-yKp`M zjHO z0ZL(Y%%$i;JV$s;L8u8Y*Ka_6BLihX-XC`VuB#-*voOicBJjoamXNgSbQ%H%30Zo` zT=dtnQ1F$4XbhNeQ?XaUU^qai?Igr5GnlmdE$jg-4amT7aCgn!g_BPVz{z0!>aUHb zJ5w-kEutz)aze)*{*Z+9{g}SPrl;!QtW*(Fd<-Fe5DJ_8q^}K0Y~A2lDp0&!8ySVF z1d6w68c~rg+BV4LtnU3tl8qKZ;EsH@E%4DWewWv+ELU2L@isMQ)yAv}4qe8`aoM4=f~7YM7Ab zz>ilfEh-MvAG5d=xcPE3$!~=*qm%o5r-GS-$cW>pVAF8>mfM&~59m6zhMJYMT{G1n z*0O@UgzaI8WxOW;ljaHgt|C`osp#|0Z1b%6I9*pm^qsbEO3~<-XB83uxry~JlY{m? zxww#=28(D^PM&TR{D9)*ES0l$LtJc4Hma@3}zu;Bwm^_ZFyXVvPVHjYg*uuxHssQI#@J< zQSgC!pClA(L2$a4Jg<6s@Tp>)#boLWoQAVqp>kmA)h!Bm1h?dq~E;ps#|rwx2)1rcKKMEaa=q5%clas2qN0)s@@0zuhArhu~%tf-Z0VC`J3#B8mQM5sp_ zPXG+W6__Yb98s$?1bxb>z$;*rf-wlNe=MKp^3KZyCAKJ;VjBgciiw{ZPl<11B5rU* z%}?40vcB@;&=z7hx!EsxlezS7x-1+o4UDweu9jS~HaQkVoGNKL=2ayDcRg70(HJu1 zC?=qC1FrLQR+Bhwb-Th#pUu{ou3y@dUt4T9^`mq;~peCXql2&{xuj8um=&GoWwlMUtHcAnFJeEork9Xe3P$1 z&z;`hq51&L$*eI0UYULtSN%uX(4;L;Lui0M^+6&UGy7XL|J~ZcnMe6H`6J6j67L0X zkFMLPvt-EEv|!+uR!yn&u>>VvC_8nKsN?$b!}K(u&DeE64}Uq?o=Y~f_)M_0Jd-cf z7kVUsXAk113?I3#AhC(pgQ9|s!&K=wsH%^Jo$ z_n{5Whk!x zh`*TTx3(5>tm(YpDWA*ScW%)}W;ju2d_n_X(L-Rc)n1LwpWi^x1R4`@H`3eA0kNGG4{v#}NpPKwP`>CG;Lh0)xP7HydqJ+AsPbqE zS}1T5L4JZ1p{3qwS7zBx4ZM~CSv(DSx-aA~ibN3mj8VPsAK>=5#frK7f#6-kMVmA7@V(>alRUeJ_?plH@aW7Z6bzEtLJ)K1^$vof9MBA1bH!CIQ(1F30jc6sB00&O4K-IdwM^m90KAlXc+Fiz2jWNK zR^aS#dZ4SS{i9nYA5HL=r?A1A1WxyfQ)Q6tH+p?s5B3n` zTY91dmLn)&~*;i&Ll^n4EK18v546>H=aDk96C}=n~*;2%&KuJo?C|`L8%EqkA^?MVvj@39 zmbeEw)k}A#5D9H&A)q<2V~$cyT#<1)8xt2?rNMqwS+Ql^Z$pA_dyMk|j-Lns2esq* zGA<+?yOp%;bI?Q}jJJXbatqVs9^NnIr_5WZl#<-Nk#<|F=j11SLkF5%VgdE1N0M5A z!pTE_Bf%S@fTEt$?uRdDCjQ3GKLZa(t(|g&Zg#i-Tb`N@-X=zq6IMA_`Al;IcOf!VSrjSiRjL0MjaJ{~uNZSBT(xUwIOT;t* zU(OBw+mf39QxkZIfpSFMI0^ooaHdM9mzVEOAlkC_1us~EjdYQ)_f)`2K!ukJ4>0J+ zM^-x8-U@gjIS1I<8pq1E0qZxN)XlHw}Xv5^PJE( zu=w`o#L;(wDkM&pNKPC?+0SN2g#65k7pqGWGOij?<(+4BG9Z^OO+xC1t9I`(X?J#x z4YT+ASdLcd8dtWgqC@DuaB*UIMs5&2NayN^wS*RO%&mQM!AD7U+5OhQ_LUfqKKviJXtqSa=*dJ<_lvIeR&kQ}JE!lx5VCE$7aEeG( zBGqvY^4~nBP3gZ$Dx#dSmZUEM`uhZNIjcL*sf?G_-gjopL0SD}xcc(h`egT$9EWHa zX@-Mx+YxJ=#9S)d7`QQH*C2nx2Q(dIH5TqnEBB5e<+CBkH?8~h<<*94CC(1J)z7Ns z2qn7c3SaejWq$=*nao-r;*KaWqS(t?5C+7Z9C5!y9a_w7wGgiQ2eM~Pz71v8A=)G zrTI@D)6tMjSdJwE`&8aPH^R0`fNdnc<=(bHF;jE%ZnnoaaTy%~Z`Aaoay6wU(bAih zF4BkSuo;O*FsPY!c&R+T(VnNoSAI&a{ySb~q-;N*fl$bEP{*&hq|<@71c%L?&dyzy@--5*IDM zNCbYAjw>@xll0Yp{|J@7g2WZM1_MEz9pj?U)z>t=*@?v zlRk9PECb>g*f9$}XAevcY(NY2H&2uYHPyb$xQQU^BSU|>au8IrKIIsYqdAMf7hOK` zyD#bpG&yulW&(v*A+Y@J1~9>iHq?j!G)1KT;&0yQ?DO3M)gzc#*@%;LyR7O;9j}H(bn^AD! zRr7tz-qE0(s3CDh%h7{2RoBM(pBU5H4K08DaMbcfyT>Cd!>pyTHg^8TnX%`p`gNo=V(4 z*n5yG9PS7W<3U6yD?Oh?CVx|^V`9;MQyb@?`2B%Wtn;xGPxdFxe@r-f*^W7`^YfR2 z2TTz6zw^uDb8pcf6T+XvOu>)MWgw!;-Q*-JoBi8zZD<|*zlh0AOvLpz@#>z-<+~P{ zG^F!kAvWs`g#IbZ-l*zdQuO7L`00BS2Tvs9Rx?g0e#EEkNWEWlY43SE1t|VtSY&aVSA1Gg633GoLak=uv17!KI^%~PR7&AUJo3OD#D$(sKJ`Jb$)h+ zYbQ_b>Z-MGek)h%H>GPjcK&0j%gtfsWXCz%A0O<7v7c3HYvrVL!szXe2eYmj8CZ7) z91OL;o~(vuX?ie-bU<9DKXt>&w-w!+SMPSQ39sUTM=y=+t+Q?@`WCI-Kz$P& zSY8r>b{}=~yk@9%puMk}{kvd?5>Y%hMrFoE{oYa7#Er%`9EM*%TfU!F%J{$@@Pobc zOy16Q!aLDD0i&FUJONIRV$!^IxKlS33boY8Ok>yDZZ}PR`KR$YBBF2I#YM8$4y{^w!`I2BhsoGgH z*X8azD)&uPU@!46M{3(@t|k$=ZpNv)qOirNzS+HNn$7I{GBH=KG#WjbJldvxMe(ZX zbkF-A17n-YdROvXAIhsuGENG8T^w;yR3rK|<_wZQGPn7o9ujD?n!jCY(SA0bD0i$1`S%vSpsd2AB*R|4pkw$9__~zId z`MJ}9ho;<$y1U2Owp?451E*?cDPzPYqVr4*ZSZ)Pdv5|xXDeGSI+%;LQKfyP6TNTq zBqo+~MQKSpxM+zObOIAkwqmAiWFf>b5#Y&B9JdZeQ@)#ak-Ykro!-~Xk}b9lECoHp z88d7R`3CVT?=T4}-5r{%Va4PqLB4o}lu>!W^B&}Gz;1)*%I%@A^P%!5ai97fg7g}j z_{T#b0d5Qjti3rPF!am!-G>@KV&<<(3tghVg>Tu2H66Sq)?_94#X8jT&XgqdqHG5= z;L^SyshVe_Bp))uQZJaC|C%m5wZ_7)7@G4tFVoF$+|qEU!Bxy9xxUUg;}dUdr{63-=!F5 z8wzXjOp0Y}pf6nCQB(fm25XFpI~GVUi4?z_c(Q!GoyCdmQf!ihVVbxy$FnJPUPb^4iB|Yb4!Pekx(xGzxZI zNczl^{YD;)vtxcP=rvFFf~gw{$F2$%dnCMl^ZjA)b>1kjLB6M+vh>w@womt_GVgra zY7;5a+C!o*4Ln$0&t~YEs>q#tYPw=(j4MOswcq1%3n9H7TYWY*eB||2jK}xf!wage zazrlJ}MiG+iM1!@p#EQP(_O!Wf$3XZZ9E{4p=E{tEDDfZ!%;!t2Kj-sr ziqXd1)z!|;+w1pK(~rBIn$I$}FNZdSl7f7atDQ}g*^3O0jo*5;MDyjcDiGn@rfvj+n2MHOz;tm6Hb)AH2R%y00jIzmP$d+uJb?@~&%;eESE*=RCrd z5BB9Y5$yZ|QbsJa4L+U7y)e43=F<{a*yq{<9p5#VFK%U*ol8FNp27b*fcE8UiI9{B zwpyP0C#MgmpI3To!mA|SIg-ZD?tfeD{dtjHxAWqGp9?o+YJNBg(vI&NV9!^jzCLr8 z;H9LnB{-eSwKw)qx&}J;;qraxD|si0%8NJT?(W-bRbI-vQ|LV zneD^8lG-1n^y7p`Bj5ajl{VGq9WxpG!+fa@A8m0~<9(x0Y2)#z=OaQ7sk0s7BP=Gy zw#MNV&&&snIZH}ElpMCZ(tgc!+Q90HiScPM#pv3Mum#bI5;gNa$H5`l>!wM@9Gx+r zo|WIptBrYCVqh|uh~5hl$cp@VQt&Ogt9`p=%{NC^Ky^A6GT&aqzMQTQhP*w<9;BTR=@E<|g8bIN?0?lRw#WG`--%$53Jf7Q+2 z6AkrISGq43;>}Iv95O>md|KSX3;UN|mT|YKe}DQ__%8EIi*BKg+r%|@kM34F+&*Ud zcWtN4B;U=vrQKfFysS~cE>tnO_>4~TeqPAYM{RzhR92paoNHst zv%K9DZ}l$7-SeUE5^VY1kGDbbG|c^8O`i>>3+mN3Sw@c*9?z4>HOQT`J$bt?EBtFi z0JS}k%lqx=#N*V9j9TjMqa3&1IH)Pp^dHL&N?7}+_M_P+0iqfQL1rZggS9!(D`cxK_9Q@Fo6mtz!V18_|xM(P^kz{ zI~OmQb%3&y3jx~+oKYe7MfJL*pl>cQXS*6(G1<}tnP?(Y2a+W&4nwePghA3+1= zK|L&gss#d(qW`U)KiBYUBeXR`Z8AZjH{i+sm%^f;Gr+6xa=7JbXKUo``O~%d_xe9; z=ks literal 0 HcmV?d00001 -- 2.39.5