From de29d38f811aeb1cce82fa61b9205aa811215c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=84=E9=94=90=E6=B3=A2?= <10549902+zhuangruibo@user.noreply.gitee.com> Date: Wed, 15 Apr 2026 23:57:20 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99=E6=8E=A5=E5=8F=A3=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E5=A4=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zdxt/enforcementcode/util/QrCodeUtil.java | 482 ++++++++++++++++++ .../enforcementcode/util/ZdxtFileUtil.java | 476 +++++++++++++++++ .../images/enterprise_pub_qrcode_bg.jpg | Bin 0 -> 306288 bytes .../images/enterprise_pub_qrcode_logo.jpg | Bin 0 -> 11087 bytes .../009-修改执法码相关的表.sql | 3 + .../java/org/dromara/test/QrCodeUtilTest.java | 304 +++++++++++ .../org/dromara/test/ZdxtFileUtilTest.java | 194 +++++++ 7 files changed, 1459 insertions(+) create mode 100644 ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/util/QrCodeUtil.java create mode 100644 ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/util/ZdxtFileUtil.java create mode 100644 ruoyi-modules/zdxt-enforcement-code/src/main/resources/images/enterprise_pub_qrcode_bg.jpg create mode 100644 ruoyi-modules/zdxt-enforcement-code/src/main/resources/images/enterprise_pub_qrcode_logo.jpg create mode 100644 zdxt-web-server/zdxt-admin-web-server/src/test/java/org/dromara/test/QrCodeUtilTest.java create mode 100644 zdxt-web-server/zdxt-admin-web-server/src/test/java/org/dromara/test/ZdxtFileUtilTest.java diff --git a/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/util/QrCodeUtil.java b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/util/QrCodeUtil.java new file mode 100644 index 0000000..3e6c6f1 --- /dev/null +++ b/ruoyi-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/util/QrCodeUtil.java @@ -0,0 +1,482 @@ +package com.zdxt.enforcementcode.util; + +import cn.hutool.core.util.IdUtil; +import com.google.zxing.*; +import com.google.zxing.client.j2se.BufferedImageLuminanceSource; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.common.HybridBinarizer; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.zdxt.enforcementcode.domain.bo.QrCodeGenerateHistoryBo; +import com.zdxt.enforcementcode.domain.bo.ZdxtFileBo; +import com.zdxt.enforcementcode.domain.vo.QrCodeGenerateHistoryVo; +import com.zdxt.enforcementcode.service.IQrCodeGenerateHistoryService; +import com.zdxt.enforcementcode.service.IZdxtFileService; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.oss.core.OssClient; +import org.dromara.common.oss.entity.UploadResult; +import org.dromara.common.oss.factory.OssFactory; +import org.dromara.common.satoken.utils.LoginHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Date; +import java.util.Hashtable; + +/** + * 二维码生成工具类 + *
+ * 提供二维码的生成、解析、本地存储以及 OSS 上传功能。 + * 生成后自动保存附件记录(zdxt_file 表)和二维码生成历史(qr_code_generate_history 表)。 + * 路径处理兼容 Windows 和 Linux。 + *
+ * + * @author zdxt + */ +@Slf4j +@Component +public class QrCodeUtil { + + private static IQrCodeGenerateHistoryService historyService; + private static IZdxtFileService fileService; + + @Autowired + public void setHistoryService(IQrCodeGenerateHistoryService historyService) { + QrCodeUtil.historyService = historyService; + } + + @Autowired + public void setFileService(IZdxtFileService fileService) { + QrCodeUtil.fileService = fileService; + } + + // ==================== 常量 ==================== + + private static final String CHARSET = "utf-8"; + private static final String IMAGE_FORMAT = "jpg"; + private static final String IMAGE_SUFFIX = ".jpg"; + private static final String IMAGE_CONTENT_TYPE = "image/jpeg"; + /** 默认二维码尺寸(像素) */ + private static final int DEFAULT_QR_SIZE = 300; + /** LOGO 最大宽度 */ + private static final int LOGO_WIDTH = 60; + /** LOGO 最大高度 */ + private static final int LOGO_HEIGHT = 60; + /** 蓝色二维码前景色 */ + private static final int BLUE_FOREGROUND_COLOR = 0xFF003399; + /** classpath 中的默认 LOGO 图片路径 */ + private static final String LOGO_CLASSPATH = "images/enterprise_pub_qrcode_logo.jpg"; + + // ==================== 生成 + 保存(核心业务方法) ==================== + + /** + * 生成标准黑白二维码,上传到 OSS,保存 zdxt_file 和 qr_code_generate_history 记录 + * + * @param content 二维码内容(通常为 URL) + * @param enterpriseNumber 企业纳税识别号 + * @param businessId 关联的业务ID + * @return 二维码生成历史记录 + */ + public static QrCodeGenerateHistoryVo generateAndSave(String content, String enterpriseNumber, + String businessId) { + return generateAndSave(content, enterpriseNumber, businessId, null, DEFAULT_QR_SIZE); + } + + /** + * 生成标准黑白二维码(可指定分类代码和尺寸),上传到 OSS 并保存记录 + * + * @param content 二维码内容 + * @param enterpriseNumber 企业纳税识别号 + * @param businessId 关联的业务ID + * @param sortCode 分类代码(可为 null) + * @param size 二维码尺寸(像素) + * @return 二维码生成历史记录 + */ + public static QrCodeGenerateHistoryVo generateAndSave(String content, String enterpriseNumber, + String businessId, String sortCode, int size) { + BufferedImage image = createQrCodeImage(content, size); + return uploadAndSaveRecords(image, enterpriseNumber, businessId, sortCode); + } + + /** + * 生成蓝色二维码(含默认 LOGO),上传到 OSS 并保存记录 + * + * @param content 二维码内容 + * @param enterpriseNumber 企业纳税识别号 + * @param businessId 关联的业务ID + * @param size 二维码尺寸(像素) + * @return 二维码生成历史记录 + */ + public static QrCodeGenerateHistoryVo generateBlueWithLogoAndSave(String content, String enterpriseNumber, + String businessId, int size) { + return generateBlueWithLogoAndSave(content, enterpriseNumber, businessId, null, size); + } + + /** + * 生成蓝色二维码(含默认 LOGO、可指定分类代码),上传到 OSS 并保存记录 + * + * @param content 二维码内容 + * @param enterpriseNumber 企业纳税识别号 + * @param businessId 关联的业务ID + * @param sortCode 分类代码(可为 null) + * @param size 二维码尺寸(像素) + * @return 二维码生成历史记录 + */ + public static QrCodeGenerateHistoryVo generateBlueWithLogoAndSave(String content, String enterpriseNumber, + String businessId, String sortCode, int size) { + BufferedImage image = createBlueQrCodeWithLogo(content, size); + return uploadAndSaveRecords(image, enterpriseNumber, businessId, sortCode); + } + + // ==================== 纯图片生成方法 ==================== + + /** + * 创建标准黑白二维码图片 + * + * @param content 二维码内容 + * @param size 二维码尺寸(像素) + * @return BufferedImage + */ + public static BufferedImage createQrCodeImage(String content, int size) { + return createQrCodeImage(content, null, size, false, 0xFF000000); + } + + /** + * 创建带 LOGO 的二维码图片(LOGO 来自本地文件路径) + * + * @param content 二维码内容 + * @param logoPath LOGO 图片的本地文件路径 + * @param size 二维码尺寸(像素) + * @param compressLogo 是否压缩 LOGO + * @return BufferedImage + */ + public static BufferedImage createQrCodeImageWithLogo(String content, String logoPath, + int size, boolean compressLogo) { + return createQrCodeImage(content, logoPath, size, compressLogo, 0xFF000000); + } + + /** + * 创建蓝色二维码图片(含 classpath 中的默认 LOGO) + * + * @param content 二维码内容 + * @param size 二维码尺寸(像素) + * @return BufferedImage + */ + public static BufferedImage createBlueQrCodeWithLogo(String content, int size) { + BufferedImage image = createQrCodeImage(content, null, size, false, BLUE_FOREGROUND_COLOR); + try (InputStream logoStream = QrCodeUtil.class.getClassLoader().getResourceAsStream(LOGO_CLASSPATH)) { + if (logoStream != null) { + insertLogoFromStream(image, logoStream, size); + } else { + log.warn("LOGO 图片未找到: {}", LOGO_CLASSPATH); + } + } catch (Exception e) { + log.warn("插入 LOGO 失败: {}", e.getMessage()); + } + return image; + } + + /** + * 解析二维码图片内容 + * + * @param file 二维码图片文件 + * @return 解析出的文本内容,解析失败返回 null + */ + public static String decodeQrCode(File file) { + try { + BufferedImage image = ImageIO.read(file); + if (image == null) { + return null; + } + BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image); + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + Hashtable+ * 使用系统临时目录,保证在 Windows 和 Linux 下都能正确运行: + *
+ * 封装文件本地存储 + 附件记录(zdxt_file 表)的统一操作, + * 独立于 RuoYi OSS,文件直接保存到本地磁盘。 + * 供业务模块直接调用,简化文件处理流程。 + *
+ *+ * 配置项(application.yml): + *
+ * zdxt: + * file: + * upload-path: D:/zdxt/upload # 文件存储根路径(Windows 示例) + * url-prefix: /upload # 文件访问 URL 前缀 + *+ * + * + * @author zdxt + */ +@Slf4j +@Component +public class ZdxtFileUtil { + + private static IZdxtFileService fileService; + + /** 文件存储根路径 */ + private static String uploadBasePath; + + /** 文件访问 URL 前缀 */ + private static String uploadUrlPrefix; + + private static final DateTimeFormatter DATE_DIR_FORMAT = DateTimeFormatter.ofPattern("yyyy/MM/dd"); + + @Autowired + public void setFileService(IZdxtFileService fileService) { + ZdxtFileUtil.fileService = fileService; + } + + @Value("${zdxt.file.upload-path:#{T(java.lang.System).getProperty('user.dir') + '/upload'}}") + public void setUploadBasePath(String path) { + ZdxtFileUtil.uploadBasePath = path; + log.info("ZdxtFileUtil 文件存储根路径: {}", path); + } + + @Value("${zdxt.file.url-prefix:/upload}") + public void setUploadUrlPrefix(String prefix) { + ZdxtFileUtil.uploadUrlPrefix = prefix; + } + + // ==================== 上传方法 ==================== + + /** + * 上传文件并保存附件记录(自动获取当前登录用户信息) + * + * @param file 上传的文件 + * @param businessId 业务ID(关联的业务主键) + * @return 附件视图对象 + */ + public static ZdxtFileVo uploadFile(MultipartFile file, String businessId) { + return uploadFile(file, businessId, null); + } + + /** + * 上传文件并保存附件记录,带分类代码(自动获取当前登录用户信息) + * + * @param file 上传的文件 + * @param businessId 业务ID + * @param sortCode 分类代码 + * @return 附件视图对象 + */ + public static ZdxtFileVo uploadFile(MultipartFile file, String businessId, String sortCode) { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (ObjectUtil.isNull(loginUser)) { + throw new ServiceException("用户未登录,无法上传文件"); + } + String userId = LoginHelper.getUserIdStr(); + String userName = loginUser.getNickname(); + return uploadFile(file, businessId, sortCode, userId, userName); + } + + /** + * 上传文件并保存附件记录(手动指定上传人信息) + * + * @param file 上传的文件 + * @param businessId 业务ID + * @param sortCode 分类代码(可为 null) + * @param userId 上传人员ID + * @param userName 上传人员姓名 + * @return 附件视图对象 + */ + public static ZdxtFileVo uploadFile(MultipartFile file, String businessId, String sortCode, + String userId, String userName) { + // 1. 校验文件 + validateFile(file); + + // 2. 解析文件信息 + String originalFileName = file.getOriginalFilename(); + String suffix = StringUtils.substring(originalFileName, + originalFileName.lastIndexOf("."), originalFileName.length()); + String fileType = suffix.replace(".", ""); + long fileSize = file.getSize(); + + // 3. 保存文件到本地磁盘 + String datePath = LocalDate.now().format(DATE_DIR_FORMAT); + String saveName = IdUtil.fastSimpleUUID() + suffix; + String relativePath = datePath + "/" + saveName; + Path targetDir = Paths.get(uploadBasePath, datePath); + Path targetFile = targetDir.resolve(saveName); + + try { + Files.createDirectories(targetDir); + try (InputStream is = file.getInputStream()) { + Files.copy(is, targetFile, StandardCopyOption.REPLACE_EXISTING); + } + } catch (IOException e) { + log.error("文件保存到本地失败: {}", e.getMessage(), e); + throw new ServiceException("文件上传失败: " + e.getMessage()); + } + + // 访问路径:urlPrefix + /日期路径/文件名 + String accessUrl = uploadUrlPrefix + "/" + relativePath; + + // 4. 构建附件记录并保存到数据库 + ZdxtFileBo bo = new ZdxtFileBo(); + bo.setGuid(IdUtil.fastSimpleUUID()); + bo.setBusinessId(businessId); + bo.setSortCode(sortCode); + bo.setFileName(originalFileName); + bo.setUploadPersonId(userId); + bo.setUploadPersonName(userName); + bo.setUploadPath(accessUrl); + bo.setUploadTime(new Date()); + bo.setFileType(fileType); + bo.setFileSuffix(suffix); + bo.setFileSaveName(saveName); + bo.setFileSize(String.valueOf(fileSize)); + bo.setIsDelete2(0L); + + // 填充机构信息(如果当前用户已登录) + try { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser != null) { + bo.setOrgCode(LoginHelper.getDeptId()); + bo.setOrgName(LoginHelper.getDeptName()); + bo.setSassId(LoginHelper.getTenantId()); + } + } catch (Exception ignored) { + // 未登录场景(手动传入用户信息时)忽略 + } + + Boolean success = fileService.insertByBo(bo); + if (!success) { + throw new ServiceException("保存附件记录失败"); + } + + log.info("文件上传成功: fileName={}, businessId={}, guid={}, path={}", + originalFileName, businessId, bo.getGuid(), targetFile.toAbsolutePath()); + return fileService.queryById(bo.getGuid()); + } + + /** + * 上传字节数组并保存附件记录(供内部或程序化调用,如二维码图片保存) + * + * @param data 文件字节内容 + * @param fileName 文件名(含后缀,如 qrcode.jpg) + * @param businessId 业务ID + * @param sortCode 分类代码(可为 null) + * @return 附件视图对象 + */ + public static ZdxtFileVo uploadBytes(byte[] data, String fileName, String businessId, String sortCode) { + if (data == null || data.length == 0) { + throw new ServiceException("上传内容不能为空"); + } + if (StringUtils.isBlank(fileName) || !fileName.contains(".")) { + throw new ServiceException("文件名不合法"); + } + + String suffix = fileName.substring(fileName.lastIndexOf(".")); + String fileType = suffix.replace(".", ""); + + // 保存到本地 + String datePath = LocalDate.now().format(DATE_DIR_FORMAT); + String saveName = IdUtil.fastSimpleUUID() + suffix; + String relativePath = datePath + "/" + saveName; + Path targetDir = Paths.get(uploadBasePath, datePath); + Path targetFile = targetDir.resolve(saveName); + + try { + Files.createDirectories(targetDir); + Files.write(targetFile, data); + } catch (IOException e) { + log.error("字节数据保存到本地失败: {}", e.getMessage(), e); + throw new ServiceException("文件保存失败: " + e.getMessage()); + } + + String accessUrl = uploadUrlPrefix + "/" + relativePath; + + ZdxtFileBo bo = new ZdxtFileBo(); + bo.setGuid(IdUtil.fastSimpleUUID()); + bo.setBusinessId(businessId); + bo.setSortCode(sortCode); + bo.setFileName(fileName); + bo.setUploadPath(accessUrl); + bo.setUploadTime(new Date()); + bo.setFileType(fileType); + bo.setFileSuffix(suffix); + bo.setFileSaveName(saveName); + bo.setFileSize(String.valueOf(data.length)); + bo.setIsDelete2(0L); + + // 填充登录用户信息(如可用) + try { + LoginUser loginUser = LoginHelper.getLoginUser(); + if (loginUser != null) { + bo.setUploadPersonId(LoginHelper.getUserIdStr()); + bo.setUploadPersonName(loginUser.getNickname()); + bo.setOrgCode(LoginHelper.getDeptId()); + bo.setOrgName(LoginHelper.getDeptName()); + bo.setSassId(LoginHelper.getTenantId()); + } + } catch (Exception ignored) { + } + + Boolean success = fileService.insertByBo(bo); + if (!success) { + throw new ServiceException("保存附件记录失败"); + } + + log.info("字节数据上传成功: fileName={}, businessId={}, guid={}", fileName, businessId, bo.getGuid()); + return fileService.queryById(bo.getGuid()); + } + + /** + * 批量上传文件 + * + * @param files 文件数组 + * @param businessId 业务ID + * @return 附件视图对象列表 + */ + public static List
G>r9I>ja<@ieNl z%4IN7sMJp9>gNgknA1cP`fcM>pw#?+I^a7u*;0dhImU!2Lj=pq>cahA#0%Kr?i^@2 z1wc}kSJLT>jv#U&Fa3%I^Pfofh(F+h#p=T*^zxtnEOxw@jmR18a=Jjp3G-eeqD^!&$M&K+i+!O{naTa z976@{K+f>dlak~ 02rEunaj{b`Wnwc6KT#JTW18h^!cQ9+Xxo=rpP a zcNq^3LLFE{l^vmj8`Np0E}3%NgWHkHuk3Zrd4eSmJF_`5uWrV+Bm2$^e&5ZoFJ6!0 z6KSJSw`DFkkYNHDhL8RBPaLv88m9z}{zT7cVy_artuama5#h1{_h zmcVqDM&rj6seZR-G2GD4Wn8E11Jk^dp4b(ybhjw;&ruunb-F%~H2{48Yxec^KeAd= zRErIVE-*QVzu02VY<+nmQV@LDI`)pv66cW^F#&uR&spD~%*wrSXfq`^=iRmGYMLR- zA?Oe<@6*&pGB0~yYg?0M%!tY;iI@ ll zxl8hKgG_D%KE>*e`}Y?fvR*Chl51!5%$y@vSh}k5no(q z-N-foXgbPW0NW?_-$#u(*)Y6+smW`U`q4hQ-$(C9=%E|@5fHv}|Cx+gTv2XWc4;K* zNQNeWA}sX(A7$%5O4on0@}ae9bQ#r1aY`~;7N=%gNII@kJ@VFra)>3`JT9v2jp#;o zh4w>&;bl~w@#J^Pc1F9MasNeA8jO`l7j^CV25E-TVXiOHGh%x#y@NEo%z9et^TLjM zvU4cqdV1PS^Z775zGQj_FDjK-D<)mqS%hi{F6y$#BKHWuc* >y(f`H6_5UaXR zXKaG#1*i@z$+VW yXf_| zKYD!&X&mnH4GHYB2Crs(#kX2TOeRtayzqbHjA3JdIDi24ZSyba5xTI7*!24;i39+V zEWDfRlld4fN7zkR$vhwS*7@ld5;@Bl?&!{JE|v{o9r2lwY9-__k<|PY+o>!FmY9Xw zB6jh{6Fzrbe?wdHFDB-9SG+i4;2(O^k4G~>C!V(HN4mm?CsYNOvn1;GmXW8yxgO`P z)OESKpBZbEwt2B9yKNo~xnp&$l{!XZ`9@|DZ%u)VSS!g7N?m!^fi>!R?=(HeYEp*L z)4}0Ws8Rd{14KJ~aOBUN=mmAZ7cdbP+2#%G5Rs^&!lm|AaBmLRlXpe^#qRUpzX!Ga zvQKa1G@~mRuL%d}nIOuOu~X3Z$AFK2s#~xU0O6I;D8^DZ74Z#sQ62`4!Xh4ZTsln% z=gaC~Obgn6o|6s~L^5*8(nD1eKl9_WXTPoerf-q~1)>|GQ$g-Uiw6^;@SKlh3#<#S zJ}np(5#OPAI;l}lD?^u-X?bXbyF%(O4J5CQHP?eFo`0hMuF9j!X!QnEKC7ng3M1b- z*x8z>FxH{AwzM^}sr?cCH9~Yh61y`wMFi_+{+Pg1_HG8@mC*hdBwel64qkCavq7{! zSF96M0n6dRaj>C$gseZu5Vm{_){4c3X*Tn%dE4Ui#SH J^Fy#~)zfUQ8IHiG4B zYWgN`W$4l%Y%f1s(^Q`lSz_rlP_mzBU_E)HiFttt!G| njGxgHhs3~rRV(*mJ6jYgrri5>TGXs`vp0B&T>aPdAjp2qlV zUY&>4gS&MS!e)#H6) IF|GWN;1B!H_Tso5b`w?v=s5Ak{%RJw7bYS^w>r zUHLQcBa1F9lc1aSBgob-7^(Tbp!ZB(a1mwCc-+|GE#%Z261V#?Q4OktN7uFibuAJ4 z9>Ysbg1W7K3Bbc`y|XTPEN*J@>k1@_wIBmro48X-uA=MZy+bqgyrLHZ`!xnrUu(d5 zR_|`|;@zhsPW$=s3Pn1u$lUm_uTB; c zLdUcjD_f_mZ|$?AZJX-AEg{j6=9V6<+6LLRr|-ozDDPxRGYi4A{k4ScdY4Z>C8P?G zIuIG Kt9b*Atd6yy)uOA z&+T2)t?~khCcys*0BQzhm8K=6tMVIz^OKWU1_)&AG|Q2q2`MfMin}}5c`8ViO$gId zRKLY$!OG2TPf@nrBy13h?qOcoFlb?et-LQJ678dDZv(chN`yto;@E(E{RMM Wp+BV zzOlNPGzKHzDu})u7aO^Zm)^C?p10f^@8rDQRQ*~d(Nik1U{NNLz%ha(k|$b^V1fOS zgxuqSBNHrrMaPxK8GvBNZDqmF2dp1Dyjg(rK2(1=(fkVFizbLNcsko~O{I4JbpoLI zdJ$OM<=_{i_Dr|jozC~3aQ`MQKrX| mR(F|V85rVwmO0BFJ z-nc)Um1JcE&K`obgL4*64Ee1zzxp(qkd)v%G*2d-80YMtXMA3Z?oE3su>u!YE3p4# z%y?7C|LB8gS&E0RYy;Paf>Hb@;!XI~8}z+$&dYh%jCaT;Nzg5sKugCdj=6`B;G;4j zkG{q2cp1=^%niQwB~HeNO*>oh*803>HXBT*5~zu~d=L5*N6KTlQ!y!?b6PV%KW;CQ zO1Beu9yK*2_seuO(zm#^tXLv%!Sj^pkK$wQt0 u*|D+uo}d^(R#3kgn_QbAJ7WeE(=^8y&Y!URqfn{eOp2@i=7Eg!N% XD_pGF559HXYI(D{vt!>K~tr$rSS zajg|5zYA4mC@LG0r8cg_5Gsq2GJRGpZ}8~d$rR0A1`tI1vKPr6?J$n18co8ra~zs! zF2Um!L&FZ Q-5X+jI3-1B@y#}&%5p6dO
MT;e11?(+%H7Z zeWsfnX{*GMXI8pAc;RONUmi|tVk=5gpOJaJvfKO@#MwZIvPCXPqg-=Sb>1_#q~ZGd zXx>*9Wcv$p#WPwd+HRC%sqin;E`L!{q_V8!=DS73^Yc7G&}zWuOJ0@u%ikBBr?8Rf z?2;692@^SKwSZ4U^URXGLp&BLV)W-cnymBx*8sm?Wnb<$#xVtXaej4GLKQLH^XB<= zl9RNNy0wgS(TFz83{W?;Y1aOEng9Pol7vtL^bl|&b(D5V=}J1cRPz+WHyK--t)%Dt zgen9$@Xj=~_*lIU^P#pss)j*1M3^oEE;I|K6lQG@;YSMx5D~->b0rd8%P~D30^X5t ze 2;C3wFBP)t z1^BUHbH!?wH+0+1OA7yZc!8*PP}Z$yOcd%f3?Eo>pJ1pIC6v}mT@;4;UzMd0w1OG7 z4A@t9E=bVek8Oxh@Y#L^7C-r@p4L&)LmZWJ>wya(^~Tl=(CkV8*P@IctKTyJ&ZPyi zVq+ax#oc4rGGm}P1}LtC?!i<^V11!L3{y>HBX9^*DA2#BPbdl$!7VtPSQGyXa<&BM zQY^rvFuRj}Oo}io(7=ChfTx6i1i)rW0G>&p+P8l}Hlu$*a3~C^KH|;s)%Qf6rdV-> zn|IY3AmTvcF#rt!R%` ~MrpUz`R`yU3p&$alk@-j80-i;y9vRP6kyF>9gqMg zcrg$rFUq*x=4{)NG3{7=s()9M9lK0#@~==EN&}8%O2j^^K&*R0s-%P3kY&KYa9)^D z{sln_VXHT0z`?Ty^m57oO~$=K81f72;u*0dy`z8cFNi?x@&p%x9HJw;fpu#CXMnZ; zg6g6SP@nDpjrqS &Abo0L;6M2jlAb2DDV3a7(CZiQ}V+jya&iC$j zC`nT`=U)^3I`akAIgV)t&=WxSfO>xZ8}6L-M8v8L-lY|?@JjR*&`8LIIr;w#6l_~< zxR4Lc)`ns;Z?0|%CVnfOA|xlM8Tc>s{_}LD%KmE~W+((8QORm$@mnI8xwJ |r4gNE0`4B`X0akD7KXSC<^%USJC}R)=5+Tvn9jWVeh(9ebbI8LedzAcN&@EL& z>l?p?LO $VI-h30ip^wn5FfPS^rIsgN7^iQOZQ!&K85u8bXL5tvY4;<9U ztsOA{WBl5Gd13^JAS+3)!Gde>(@O_r!;ME#8!EKo!T^Y~Lo5K%xiP|20 Y6l z9A{O*-{5uLzQ_J_lxSfs;Kjeuc7&W^fi;p(AduO%^ 5zoL_0EjC9?MYV`5Ixox@S$U rSmy4CL {)_stLfAmX&F5mtOs%X)gDwqT8-D ztli Ab-CS340sIk4ne)8 $fY3? z+C`!yp^WlYu2*4-DipP|eM11gwg@?@$JV@P#S;n9h3aYiKGHIpJfBu^#kqE;c}RMp zhR|ZnO?x?mgdyEb8tNJZ>3mP5lj{=fyU4zA3V-yZa?ReE_8F&!lVwEDk)WulM)nby zysM~W>Q-lySXRP7f!EVN(Z{42AEqU+cY41FE6PIVHNuX5raYtNtZu7_cBak%&QgzY zN>bS4(syCqTMg4|be+r5+8*CqFI#i=Y#_ZQv(ndz8(8r_cGrc9xK!`=7E^k#BhyJn z&C-Cb?M9ZL&aBSN+?@#dQbyMc9=Q&ATW>1HKe8pARKItl0lws2gFwF7lZ*ynSfxS{ z*gDM?`&-*kkqF2(n!=gdSQ3knbj$y+L`8e>`}S5t9C=aVdwbAzQO^VG6v)5&vDaEv zgP+cGJcNREkiRuuTr~~qYf{ru34VJvK!q_S3o1h4cF8-~QBAu&p^3HwgRri;9^g)Y zlUw>)NDDVC!c8iYN~MkaHt{;HXf)k;fS>PjW9BM5W%>E|Ovn^MdfKe?CDJ?y*m|N# z%`rR7Hxo0Re&k9`nL1eZlnSHZBkUH|WO81KY**&Z%~FN_1is4pF)@9YwZ_iwTxk=G z1m#OSHHwhN{35dw;g vV>r>LS}<*;hyoDMAhxgHJ%DyShZgJs|j$fRGt! zTKPisF}&MJ(bt+%(E1Sjsw}hs3aGa~ym(f^RLk><>XybRF#2V8y1w+Ll>+x?4N3FB z63>W!Tl<#NkxLo$?ESQwPw&~89skX{fUWE@Cu7R;<~S+%RQnm$?0j`kYoe6lXW*dz zr_&>^IEDJcr5=NPK%A@;i7DvrIeTF?^bLf}*(umE+TGC#F5z-gryx;fSb~$2zcSp; z3K^rQkd`!{%}QFJFs$g;mH !T=LDByESdVU*tT;p{ ^<=kM+(%waXmH1@M%&i4aAROD?JGG7dp{%Oc-X(j{p6qJ2LqI%B)I?GcjGE z*u1ln_gP;*`zhfkH{7^cScGebfQ=*6O7Pc1=|4nFaoJXWFD7%wd92lVyZi7P+LZ+> zMf~>_gcN>FT`b9oaDS|QqVW7$oe(4x>y5KG^9v1JQ;@hiB)stAJ4Ze)LNjSwVipI@ z2ACDopU^%Iq}9{*W;bn|BgZ`Lt7cIp< 89Jh JT~{jGX~svGB0N$9qzI-F68n dK&ox+Ds4Ho}#{(%ge(aFL54iaCBGuv-yk2-f782Ta1|r zMFk2=hnquYkM&7z@fW%rZy6TEogc*t_;`ge+VC6N<=myZDN9d$nO=EO;T=H(CpRhm z3) Tc`x1=Dy)bemvTk5#}Cf@1tY`kO>II5PTTc4fINp`kv$ z5|@%dJ>&AacWdI4sjH>8W&jmby9H2}&P0B!Hu^Dh`SI-WcX`NnLn5Qk;mF47fOaJO z8wkK3Nz4G}v-zfuR1T(yTVbW7PSl3|e4vaBP}{k b`lz8$6w&r5VT=CWQ z>@Ub6B-cJdTL!@TewFWL*voCPOXZR>ZBDz{mb({JBz$atmCu6-!@|0-51b{gVNdU( zZO0~^*CIT8T@v%bkzP+Q$3zD?d$~0(?|*l{BYdEA`ys_4be=mX2(MmSB=M^49@c0` z2`HEXrggak+ti%_=mTEv+DB4RZVA6$XS%T@(UVSkgwFIMP}I_@fAY@KTfil8H82H! zie+X6OpvyZBLk}|Pryp3_!@fDhItz27hy5q;iQY{<2djE$+{(BWb+S55Zy3fSKyp= z_` 4x%UV0jE^3%r;$TT8zb;o!#EBjP*PAsNjR-H1^c7@Gi7lkW;r zK|S-vkD1EvP48cjx5UikP5HN#4O!sZcKsLxxVsOtL>uHLWenNMQKgQ$9Jon&t=YC3 z;^o`+Gy*U;in76NnW*%(BZ~8h6QwRO^zzda23Sk}fZo?cNok^+N#oUk*lsMZ6)&K* zBDtMIfR3@Xk)k=Le4bW-%j2FGM1}d$Dw20-A7dXbH=4LO`X9msm1?=1Mxu)H2J|o$ z2gl^q|4IppzO|_D^pMa8E70yjD62CSh1u6}&d2o|?@zzZ#vN*o@cU(4qOfkuCfuA- zDvFBZRq0xPS%tX^#FND=$<|h&rBNe@E z7sG_`=~PT&u>ipEb|TVoK|nz!PdL$3*t*TkvS8o6#2qsN)>ZYbz>8(ay9GhGt!4ns ziEX7gayeGiQo|jBSmZ-ZxAw*^8-$>r@-f-U!mKH~>mHEYs^UO*X3oC_GfrlLFQ=;} zx^AAt0A1M~08IQi^u9#CrHc9B#HvBANvKr<7}EY@c28I*i&; )FXc+D4P#-|@!qNbfTu7v1`-#-u`bflN8nT`LWHQcB|IW5>F5SPe+}+nOc4 z;1|fn6{J8sw2B_mtu#8}ehDJ!OQ|j@Kw@8|{z~1aV|4p%fn>hDqQpYUTGJhQcc*v@ zw9J(*9y9sW`z7!D?w`rzunCA>5N#pyf^rkDt{xITF5i3U?4Mn&>Mg0&y70{>mQiLx z;YD$A?9mR6bdg929)@on_xjn$%i3V-Cx6;rB?2Z}#g*Dh%j>+ztc^Mygz6H)#q4=m zY0|oUn{?cmFL|5S!E0MG(ifRfl^`eu$Q%0GvGaNa$Uy-@(S8I&hjk2<6J?0kIn~v* zGTGnVdPuE-&)b$odV W>%+-vK*o|)=?TtA!JcNiJI6h7wmaOT+r?Z}xNO@`C^C8* zqA2$?9g}>Z**%R{O2a1L)8(gEIgme^X2J}V3=|i|-0kW+v?XI*GcMb?ZEG{ovlkj% zJWKBfH7di@Cbmcj_Zqp;9p}8FZ~Hg)KEY>O8IipOPQn6tlAZ-ibt$ix9fIfeQ6osW zoMX$$0ZClfyKla9SG>D|Dsjz>rI$^zX!H!4E_ZRB(A(dVvYp1C{V4+cXdyqMfOC2) z>O2>MtTYI;&ttiYiQqJBOM!UkE}Q|D|Bq_VoQl*>a8s23%IR%hfk=*t*n}TFA4%EX zK4h+j$NK?H*zXD}?rfQ->S?UNBOe$8Z=dEL^KshYTn|%*%>Cj4lX&!m3~jt2xqT*q zwk%ekK{p#jUEdq3;JJJ)17_aFJ1aF>gFUUg8KKdIwcEz4g#=#IQpWXO^ # z#$?c7uf7zSvZb61kEz6RGu)E{Re-RH*krq=-u|k51#R`C66xH*x2d;iVRIDrMWO4o z6@3Yi_)f?FbWF(Ovqx57Wq28Fr4^Lg3Kybu7dcUr3gs5xXSA!pm#1wT>T0VqT|MB_ zS5hm-ayvJZMaZzVb944J0t3_|AkM4#PQP`sbr CS3A%erf`-0Ju(Qzin}{!N%t@MuSWeY?JWp?Jzks}8k6YUqZN?U zP8fK12e^2nC>+KZxp)G-5D`Z_wzciu+gt-7j^$x~DLy*Ck76Pj#23f%EXO+)E;jCG zh-bjQs`oH9 DXr$@fNZ2N_uJNA}SiY0891}=*31X1)Vm@#d! z|5WU!)HKrfKhe$Weoa8JB9!n`dL#7U;a%Gi-hlOf#xF|MI;>2JjD1MBL2q5>;8j}7 zu#YZv(2NAU)h(uV2xu50{~ zg1vv}1uwgFoLDXDfKd`vB|{M_9U`tXwPtyO2R;M3=u8@rh*Bn9&7M9fawsae?BgT9 zQ+M1EIN0N&{Tz3^uE%)oOM%4!bKP|noak!?SJ`q5w)(>|5i%$w=oJ;U6JvceNwqGb z1Gp}Y)QzmUT<{CY1~j3aIx#DAC;-cQTJut{$~msdd}2JDrQ$mE;`V|fwi z?k!U>su{8b!Fm!gH>dLpkJJV;qzn8hpB`^T7aB6$I>~sfP1%hk>e%kdvd3xGQW% zf|4759%HOlr0l-Jb2FOHO$_(QOk!1?+#kIKwr%}%Nz(Lk`u`0u`1d>4F8D|Au3tRp z ;%6K0QZ zt~p1~JnXTuz|tn)S8I@r<%;EiCf@ai5(v=U_Z2ls42w7RJIx5L&fN>Vp7txSThKTn zxrjb}i yt{ zjG0*+eK~NOIW45+5fpXoBSt>=y6*z8SWOJtZbqKy#jIEmXdW6(%byTbS?2O@fQU0E zs5YgzhI3{Z@q0vR>!Lr~N?l6qoA)rI83eh~ |lx4)nkKmoBhM+kBlPh=W=lwxa1zqf*<%ZZBM z0rb!$fBP%-6cdjTCHo8I#kSj8gj#Vf%;~N{W{;5f1x3z}FSne>K-7>bp)b^&`HG6{ zAaLa~_w__@TbT^Zb!|=>;}Vab)UR|8I1%n-) 8ZI`ZMs8zBGM!)sajAO|<({j#1oXn)O!l>p%usU8CScGxEubyobJ}Sk z+#a#JH-~_k#8~baE0F(0-e)C`SAb*12}o(v3pMXL8T@mTzJW1kxK@P|S>h$<#9)>` zL#E5@MqU+&p3h`Fz@opkZuu8O+0IQbo-5!=L zVagmq&^Ga%l1`uWrtM(B@{^x|rVoUi6 XV=C4Mhz*e+{JnqZd!KH<_FC;ztAa!? z`I|q{k8DeAJ5B>(zExLI1x9)w-2#91k-c*JP0U~DG2MDM?&wFG?`)|?@N9<6klvRC z;WYKij>tZdpiiM>G?y0@qOKfofA$3oDN&hcwO`~#;L!4)7sTieEn3bhgDCEVM<(ZP z-l{}-#heku%}5BZZwJu!`psw zX68LBnbioH1KbS;*p~aFCV@9ow;K07YiZlk?@Zu6hsllIvR?44S;+K3wDso#QZL=W zr+@s^vuA`8zzG(q??ix0u!&OLu+=S@l%111QD-;fsh{^Et)&k$T3DWyEP&JLCJ8sp z5puN=;W%(3ZLlejCZLK*6n?^TnQ2~DVRB7nQ1!T%|FR6nM)2{qYMAJ13f$(t`<(bD zaCx|jmA?z{8Q+=j&BkpWFaZv7dHSjJn@VDZgnGs- zXa6EyQd!P78$!?5--Hb(UsI?=O;;bi&^3(0)~MDKG~u1{PAJ-msU*wr-*ujxvXxg+ z5S-Jo+XHU}i*8P)lxNu&=ujtL?`(>rrN@6(qwFpa3K?1tje5G%vuUHfGcH!-;5?pr z$ZVb07TO XZugYteOoq)!0%nbCnB4~PxD10q(+)2s~^a(UF71trJFGg4ciZ}A<( z7AfK0?x5N6FKDIx0=1vfODBK*BdUTeQ#vbA(~~r@vNN|!V>#+g8JD)lNl_)kW1O-T zC7;TtN?2v$WgZCs3ldzjlwbDd-0ylhetuofgq@g_(rcS4FeA%%at=_Cdff|Q^Vs_| zY^S*PbN2>Z#>y-L+<*POgC1PQc*giP`QeTCw4eCTg|HaAv^%=U>@aJx*&(N_dGWJ5 z+ gSxxiXN5(; MO4f(wu?p>Gq;96FjrcKyI)thVh!mPJOu3aWh8gXw)$)S*v zoEjTWtV{)0PJ*m}yoT2sl>%x>t!Ez(@%Zy)9T#}@c2ssnoVDklg&x;puSM2@(~uiW z7QbjIb;|oCL00=^1&Nld{lmtjN8=ly3jjzkQ$`b|{n2|=y4LznTH5A<2+PX{g;|!D zmDhFn_5FCv$U>xdN=<(6oAJk!s~S#j-^+mhJlo9uYO??y3#~C=+n2|had8qQQrZ*+*P4xNk1I2#N zPdEbBM0jgpPpptyI;?%ELL1Dt#e$WWAh^}($P?^L2HmfOEF?UL!@hmb(UZSU?f)?K z-qCFSfB3LYRYk2@MTb$lb}3S{6eYE{w6#iV)J&}?MeW+H+19F=8nKD3_NHPcR*jGt z5fb@4_xJZZ&vTwX!Z}VN@B6;r 3>}bDSUD)KmQrivcm+ uHI}eAiO^Ty-ziR`&1TGL=*nBUy_QxO)AOu =Djyc15ZKL~lXL!-7ATbMKAnPPyAjD5_6}vjsbw zXFjxA8s@dU9}f@yAjA2iv#_V9p5K1OgH5AYcLucb6U8GbRl&@15KP-)M_XT&uAlF1 zt?m62ETduIS6-vL@=CVxG#)3io=E&mI#zC`V@zCu`gtyVeHsw^`S5&r;`w}l T-M&f8>Jx8W5k>l6=L+p )!-8?OnQZSHSInI>P7%ey*80>`quHmG$IcT` z?dU(iVbGzDdMiZ{4B-d(45}|O+SML5XoYv>;SD|F>Y8Hj)AJ; i<|eOWS@AIjt!}S<|ofCl{m>S zNA^Doe3)+qyJ1r-`rgQ;w_MIEPBJ^oU_{SgR%yrH+eYBWaSU@zO11wdWKFO*5p0aE zMFG~lZP&(*hv|tWHR0`4z_)D97(hd-t>e|@U;B!L^&V<6_c~8k>^t(=Fn96T&_&lJ zOUv9GUwFHGoAQD-7En7JTaqH_`2I?K`~r|>=H= @w4`h$ zW8Kl*Z|rrSBZJSDp!|551Ou?iJzF!U+-)WfiebH8w1JIAkd)8-h3%WWn)Sth+QJ$E z+w#PoMf(-_?jaWbS&a*otQrz%c_lahF_+!r=-Ur6Hvc$Xsg}9+hkbM1(&x}-c*WI( z @xfqUMePE}cWmb785 z{S(GFHqbgX$QW7u{Ukmu>Wyd?>C#H3 _|1KyTuKLI>fb9dVoT_`D!Q5|(d9 z@aT 7`#m!V$7#ABk?{E`;e*@93zDF77$7OG~K_zud z?4+|Zz1;f)6Rp)UZ_5)t% %km#U5 zycOpYLT#JiSG%2MhV_@-+ft(60Zxpz=Y^btW%(qtY?bsSFH7>}T%{Vz8!!KzWta0@ zmiTs?D&M8=!5Sn@O1hj>k^qi4+aBbR8rUHkcko`@{88CQ42;wPgvV(Xz+#X4SKN4W z=Ay;*;{=ky#t^mEbS4gE1|_Zj-hI=+e~ZZD$EmGteDZEidpUIz=u=jJvt(ZSM;5x0 zqx&S_LxfQxE|F^pG)}q2N^M^HXlwrW;C=tu#Q7ISg*fsI&|CbouBZ)w$V|W@1Le!i z!jHAmirTNO`gXXQPPX0C4GDJE{|v8kw?3UUsr#x*ive4!#3ioltQw)EfxthZ??I6K zDxJkCRnd{QR^b&~xdic+XvZ^#pqQ_MqYt*4%+l;7Hp)u372jK-=d7Pb>Nsbb-@O;l zY&rKg!qiBIhSj#yvgWtRm9N<}t@=Ua$_2o*s+#oCAF)G3p#ceRo*fyhIhTX}EGd4{ zz{KuV$x`jS; )){b-pQ41y_z2;tidS>K%47A;- zTBhz{?&ZF`8zAT5J1~f`u~Za7O(e*!0pD9vf_C`SN*&g}hr|6yWgv(j2#~n|KdiOh zx=LRr^rem&FIZk*UKKUG;%&P9&~#ttXsi(_&!RS*RLKf3GOC~j1{JqibUfSNa0-6` za)gi>eq?VAe&1%IMH&NU$m0&uCG^%Xe#0=`pidOT)f*|7xZJ+9*1^KN`}g{H8r|?l zSBms`rTqSNicL5%5a^V@;*Zb3{%llMEnHkWm+jWcraLMle9BfZ`S3LAN((9{Jc3x_ zV51JXa{SfYL+jLB0v%gh@I5oDm}751M=oc-1{zu95?*LMH;|htws<_J=x!>6)Iq{C zQR8_xJg5v5bG&|HH$0*~zzO+dTg?^is_?5vuScv)&Y%)-y))_)FnYRO{Y=0;p>$wC z8Ro+vx_qvc;hwv5F>%XI70VP=4YgC8Qj-bdAfy#{jYc LbzXOY56+JO@2dk-lbq;D(M%a?+*CnU`FE%9b0p2B*u zVCnnHkr@HMl3bfUe7Mj5_Oo~r*UyA)dY$YiY5}kzeWimpl7-vokhuMqpD?33iy>mu z@aO%c6hffgIK`Wz1M<=C!eoo>YJL%ewOQ2^a$~|Lk74WSpOWCxWo D{M z3yLX0GQN4K&ov-@y&r4@A?*#?xL>y7e5P^G`K9Eh*3qICE-ZqUqJdA(Sly?A<%4IA zc)h_f oH9KM@HeFngUz0U0)a6P;#WNUGc5l?PKBxA&S>Mh)Mf;v&{PTAX zRJoHpbCOIYIxuG0Zr4sfvpY&y@Bu*9?+a5*uEoYxBB?z;ZgXGBuezY6qjDj0EMfiw z`dj}aPWD4YG=P=eq|S110N~TR9l(}K3J^{rzi7$Qown< vBnY%w7M;@O30VyR2WHv#h>aF3u#yZBAm@etB7QN*YiBlZ7uA{c@OGkBWZn z0K$o|A70sS2mRI5pL#tZ51MOkzcrh2-3mRiYLu(w_|j81-+=xG;fvUtQKH* zci3aBaHIyjEWG_c8r~q&e5 G~tY70`|2{twe2_fa&H5nntoanlmuJQ6IxnYD9f;vK|D(g})?2NdA~rcW3F%H~@H zi%anikO%BENR#04w;MK*!iO#)KZYZB41-eOKaK>f+`9Xitse*9% }dFL-|o^c>L?%p`(AEIh-8b{@w{*-oY?k| zlwuo2y_^|w^cl` m$)C9O;l|K{O7CUMS?3p#ebAbR+gK3|P9*#%Gz&!Pv BY zomaU`ReTI1XfTK1Gd*X+-emuAYxID`;=hWkEi-i+)Y D4tU2Du*uo_<{W zJU@6}TrB`Besg$NB~1PDO;2_9L+1|uI1oMP?ea%6s`&2-T#z0fusMueL(cjz=n5wD z?1+r7G}Yv&?Hv#OZrDnD{YQQlAbU&CQdqIW_C#$`-YWgDhXuYT*>&j>L7@sZX-lf_ zlvSIDHVSyz1vZN?5rbE2RLaNEw!^=-4GUqMLwv&GJu0d`ds$Xxa+5W&w%jf`KBZ$l zW5n8)%KGu$*%a0Em qpT4~bHt)6q3`7U-JG*i@ z`#$NTy`=O=K94YpU5=#E1l=SQ_aV5ffJ!9wDn6kYD`B+^elp84Z-LhH6@AJXYE$rn zjpgd|euQup$*l|q?Tsho6Tj4t1wA9Y2?Ps`Tyvw&<_Cc$3(%W-6CvZ=-vqu~hE}ML z3$^%a^xu%SLR%G-n*1u6G*2NX?+wPi`o5|77SY-^I0P&h3{X#b!*r&~{|Q3!_Q|6@ z&xf2hpzN6#V17G20SFuvKf9~o*NZi;B_QL65<(rWVI$3xo!u|?ao058EG_T8w7PT$ z$nlb?{c7y9?k$Q~el$NOr|>uqcV)di`i&Kr2`1@x)QxK$yy~2CZ5-z)ki8qEd*&s{ zd^NFSc1E~Gy%^c-68(7L)~TPug(d4EfHMf40|?l#tdzX%1HfiiH@)rzRfDXk7=hr# ze-ep#aq!do(|69x<#$LdCpzbGA&+gWhCbr<_&aXZ^*+6SH%?KpN}u^YNihDlkE3yt z0SF0-=>zPm)RdXXsYiztXvU&(Q7GI1NO2V(cAo;E$KC8(PL>Fz)F#isPi`%!foKY-9Nnn)K42p#DsqF*x+F5{I{sI1<$Y>%{+yI;n?)m5pU zww;WhMmHIkU7emZN9N?c5zcZv+E@WhjR+uBH#&)OTuAZQY#wqAEm)pe9}76GB`9ls zb1&RS>V)uDV6A$U|BU@z0KdP(`#eU3hwHH?gDKF#fU_oOVx7|X * zw@W?#XVj#hJ(bqsA3s3d0=?=%j@~J3)jb^9U(?usCM)rOI?qHwA5i7B%b @PrmLRXO62yBF(u?zAv0Tee@#B6*&xODSU0%& zw1gark>ozIekYJT_$~c`M6!;|?;>l#&yQ$wbvCokt%uX!{5JB-a ~(2F}c>D zi|AyK)vnBgqH)20FIQ)WR#g>_quc@D8fav)sKqZwt4T6l-LBtb1&>pO|0ySa@{r<; zd=$yxzBHkez}|+fUsOvwblj(~fji+>J)R+O_JFYsq$60DPC<1uT1CQJEqgQ@p1(XN z9xbyOzjEOZ;x!=H3#AC0=hs3J^nd^^4&%X#)`zzJN28Iuv2=>&ZFf&czjC<7i4gzp zkZ5R)Z7>FPOb|yHCaLb$9cKoNp7rOgC(K}lLDA8qB^g{W9MAP&5U-YKqGg(-Bf8DS z$;So06TZN2W&7DcX>_Iy8aCPaA5Ha@R#XopG=zJy793emy)!Ad{26}Tr`flzdTb9# z|M%UG*`r5e*Ki**VZ+Cv^lbfYdlDHgGTU&ycMrN1Up^OVxx7(;k#~_8pMTUN_@KGC zhT3#;v$q&PA)nv3qnx`;83W~QMvWMSO7B{ualj+~K|6Z{K^E)(QH_zr4RlU`yAjMi zg>ReuQ|mdro(Ys642o)AW`4#}n=&dsp?%}!Q-t`$;O+PM&80cL!4>uFhbNzo*-426 z2#_R!yYHSCQ7;hoiZLFamTo7DTaZGm-`+x#Et9-L#(#|**mS2T-Xg}`jd&xw)bA^m z)30&;AmILUEyjbaTC;LMaWit4Bnqaa0Q$6WQmh>aphXHQf!q-v3jp;@nD(mtuiX =CP#hkga9soW@gem@ tc4rFC)ckt( zx=?RbD)Nl_5$gXz%q? bcqqPSC>G$ zEjw^ZKn9iFF2^MT5D_*dKY1HHXWToZTsgUuV>Ys$=w5u=XT2tGnt(doI^UrTY;+!# zwrx_i#}hCCH}YKbe_b(^4Ig_Q52K}s?X&(8TBcs7C2OkZwP{ZDo zMljl*r#FEe)KCg7kgx)SxARj*raKcPrARUq(2!IV&_q;4f&U@SH^m9jl7nH=V+j z--R}RQ}~&n6)>AHxB?|#1DcpR)PayMI6r3?vrMD79i(c>(RPh6(?aONXAw6^;iWLd zRWQ?AI0r=< zpH`4Jc}Gv4xoWVmEq0bB{xOA$y{-6R@tX4~Ky=7a7f6BnL*|HaPp`oP1wFybesg#J z=3Lr2x$Wq7Uj^ufgE|SA#M{R|0ff%}nkWznR-JWYLhRZXE1rfv5YAjJG1PNQ@8XZ4 z5%-kcdrQ2Or|ZdQl{~zs|B7bsORsGL4Z9s$xb>^e3&xHUv&>7|#w!j_i%|h>rN1uz z`X^HVr={I4HC%iitb5@P?M$Nw7p%M2VcaMB!{v&h?SKHF$IzH$s1>&}nhwJEnwDvN zQYz5HE_3WTPsn|M&*fs6HcH{&Dnn>!S45EwmqH(2STX$y_Ef%pA~GIv&8v@k8^3Lq z<8t`ChK}Z<_G>efgs7O(iuLS3<4^5;fVhNa{GidK#%NY<0eo3bu29S0|ERRri*~3x zSl@?_3qP;02sTRO#YRL?RhGxh uKaSjb;%YVWq>5nX=IpdkkP`Rir(so#WANZtwW1#_ zO=Oio6AJDGRw9`6Mq|X2*4WZ};Bd&%U6uBGIfl`RHA8gr{gdN;n@u2DidF`9NipWw zonD@mNlw3|{@Xbv?r_fhP8F_w`-$pR@CWHV@WnQBQuAtK+g;eernRL`P25+xHQ|U) zd`!~`7U%E(nol?=84XLP=XLNpZq5Y(h49gwks8qMuFM)~{pZ0R74j`<6~qlo?8}^l zwh2wWIF!EkppUsTZ{Zo0{Tythvr!doL__MBqH*{5@sH{&8{8}5Wd*xc*}XjknpWRW245BXV` z+%iv{XsgxdPN1%2htI)uOSDDQcJuD{c6tUrxymuM89nZ0r>*98JHe)-g?|o}w0?G( z1c0LYiATYYLB{BR1sx;aB%|3)?b-h;kn(?pQWz7n&s`H^6I6tO$@=&Iwg=d(c`;#3 zRIIv5dZw4*L;qjTz!=aoz=kFo*?|w7he5x+apx(!0G$<{2$Z~65x %61+ z?rkTtjC6{xAVnGRUumPIJSW8$xu8?e+>5dNmq{UWN2q%1#u(VErk199y}C$Km_s03 zK`v9|wKL1JdlGr*s$;-1Fb~tWyTcUYtIzS26pfY8nC~@5Us*+O%Wf$Im@e+tDa=05 zoXh%lFH%JE-aUuVk-IQQ*k m6~60#Rh(R6ErQf=zi@sFImx;^W|Z+l*Qtl8sui~W z!L` PaHktV1SBuO&5lZUqjlo}nF59di4k9K;)0~0sk%10ugw;uB#gU{BaL@Hf4;nNq- zXYtc%=sa+vaMkA4j$JTWkpu#CoB@*N6@QW@VZR$xg#y;>WfJs>ZGft|F~K7;_nO2I zT4K#GJ q?E_xA+Q-s>|nFGIarY#=bmb zelWY~Gc7~637toyg%*lsVOC yNYwUwchoff}X%1qxSD6qC9bJ zmJb!g&L?vkueLb@!|xtsLsjP-RVl2wd@13}^)mly88VBiP5*%ag~{=ggw6Y|PT&2V za9mrK^*|!`v86*2i@90WiR&m^yMx!u*)JhLq(qPMSVm{89B+0auERT&)$VS&sC3}m zef7x9>!Fx|v_oxvDO&ksODRUI%+SW8%wv&4(c0n)E75V&`$E9|*muYphdM)ZZG%Q~ zh3R%z=>mUehj2C5(+RXO;(|AK>f7O`cc~0OtGF=`CQMlDtbs`4(Aeb|c=b$Jp zg#LH)@A+5tPQw2by-5MHe?eE}7LA{g)G@X^Hj@Rltu^??Pm1ZuU+lQdmgIfh3gRL! z-)4D)$Uv=>LTV5=Jwjd|{7TzB>JUFeErNBCqC!x}FLgEJ?rmoHB(Y)L$EF!b>0bL2 z^74Iswu5<>XW=_lNvU{U PDHlg$scTeBz2RN_0Cz5F7h6^>GQ zCWBd=%DIJVu%L^ioz7g2DLJT{y(I}=wsposih2o!3bkf3NnXh{)%J;NW*GXs%O-uj z`_%;gY6%%g ~EbcxJPvhgbt8 x-;+%>l>>cYez_p-)^z2>+O(hf+*9)@|I*Xl z>Kz)>=5^2V{}d-yM0BEIE)8XM3l8N?^%c7nb6aC)n(fQHEyhgUs*eGK<16@41H2Oo z`@k`y!yGLf1B60HAXZMpwMJx38`RpI<983w3u28rYm1n`Z=O%}O$yYXbsH@g2M#fc zIA;7Sk|t!@VJ7IpD!nz7EMfkv1?DbZXs)w+Y)v=IVr@G=lmxyV8 ?{6S^*LE)0q&XqW=#U*wvD>SiM4Iezz@aKvg2(-%WFmzW&G5JvOo?c zF}>OVGqo8F6$r7SVOBoJ9s_FESM0H>iz*JXB;`^$2nrS7A@Y^^x4v;i$uBy+t*GSK zM1Gimo-kMZxqQ6GTTD|x_S!~b%=u3>!GDlTAxzs=d$AZXH((E@!7rqr(MAG0Q`kIs zau+ktUeV%u@n`va(NG$i2e(B-l~ME7-2sZ7i_n!k9M38gQ4Q0? A< z-QC>T(?T8ZW<2P2xY>TjZ2L{NP`uNiw6)P^2U-dBYnq_G&4#9Wep#QZhudXPc0Zg@ zm?w$e6KW4?QFx=}*f!a*7SmM^w)xhKNS|}Qb4gqa>#LCSuZ|#z!?Fv~ca*O_cqowK z{})sX-)+SU6YEG&Uwj5JjP$`CD{*UWKS3-_OupdzD}4`{BmtJVq*g8iI^#3}{!SEI z$Os@^0r(_oV^x-GA-k_v&mn+t0n-~%^h}l<=xtMdZCuJaWM0G+Rd4EaesDSc&d|W? z-orCsi@pq;1z{}!oQyXDU+&|G^7uX$ EdAh$_yBjjj7=mw4zTf@% zf;PMeyMT~Q7bC+(E4K|JD*9pI8~OO2p2xp>&rD)n^&1T)vb?&4dob+$yA&IiY(&{f zzG*Ak4Jz#RYIj0Mix5*E5%d(_hm!@q9F@h-fs}w^``S%XwVeqJuMR1iD}SOpg-Y1$ z73 s!fE8)vigtiD)gh9a9k@tHbU;ne0p0oRE^+`Q?j>0C4qMu%sU9#!?Hssrh5J zTjs>u0% $>S|mt!;F)9 z$R>cN8D)_W__^Z8bJI1TU~Y2AdB+}U>SaK~$-cGf^=~6cHKNC9GGCJ!lBuJso0TK) z$X%(DAe~(pcyLgnHId4i-sUJ;-RXpMS-Gz?S?sXen4G@H(c~^-9awv$UszlQv&Mv! z;H`=7c)%j^Camn)3=(vm#3CVUJk_GI`);!#uE9ClCgYjX0&}$E<%`4deLe7%FefM+ z?N2P5wEbu@-6H*9HD^Myv@fq{d8R2a?-M`X@s@foNMU;5T~$gkz|D zFl#*@0#lDttzMZ}g@V52+SNx#%}5`|sry|X#;LE&e<;K8HE_4_|7aBE@DN}I$^dlo zZ%z1o%qU4*#o_LGtQyxJ>-^&Gv5SGfMN&v+=G{EYBr*O@08+mPco0G6G%@;h@fcf{ zeY5F&{+mL{esy&bMX9~_ ^AcNR39MxQXOqXYjusN~N`+O` z!H*VXKX|_QL)=&R!)H;Hi&i@S9clJpTb=eYa~MMqfviG#G1(>tqwNY21sM8S>ZB;l zj)**jaho7G#5%Y9q!3qR#v^z^&_T)O;fNMz%s N*UL z%aD@$?ZRyyeXRP}pw7i6BjZnXU0tZoeYu3YH+J`i`Zfs-qNjcmW2O7hv!a~LU^$mv zKSP8zN0jOJiu$6`Ekx4p_VXn0kK40@0MfSVcAlH2jtNl+vNR!H>T7DJ381-3JJFWs z(F86G!eCVIw($h%krK)B+YdQ+H>RZkCm*>C`skx`gI&<)cOAW($;*}v>4ZTCajWT6 z*XJ4>Dk^Czj!LQsk5 gAiFg>KfxmRQ z+Y1K5+7)ww-xH&qxG2&%2!JegyjD>y0sf(47i6BbQ{mARFwq7Ej@@5|n!X^gy#V%a zZ}#t-ZDRWXS{k+p?~uF~UFd;s>yEhmrHyA=r*F(^?Og0AsE@U`p&UUA5lZv>S*Tm! zayTF|>C$OHXCRiD^E3xrxw2Hb86O#`uE%P|Ij!&QH6j^QLhdgS6q@wQwi|sWrJ0dJ zz1H}{GN%pD$O((0PAWX09b~0Et^ao6OtlEicLZc}mmQP6>j2Ucn5--F@!*tCp*C)F zAyzZ5Ffea-o*Yaa xGZz9s9rB zGv~$dcksDMq5^*J8+IS9*Ed1((3_FJWM90T!${l{k$mbVoH`7>k!#In_KKe;OhLAG zbzLy;Jw^7)#}q*LMt!_+ mb D%4FTrN z!wM)rNz$Lf%4#`<8KWo?0A1K=U;2`#W|<*b`1x*Du;(VpUj223Dqc3*99Zwy0r#;W z8~k5_Q_hM9>%Vlbydx8WU6fFkx6Fx0jeazih7fHe6A-E`U-UYQl$bhc)H{N_Ox*b7 zvOIb6(qk>J -*;m6$zkuwI`I@_sJsE z0T8FkGrSnqqXQD{w{#ajQbaVbTCm*uqAAI4D-^xfb_JU(oM7ViJvB||(jx>LK%iRL zLCp1d7=!U1yUPey+2@=E<{@I$5hK$J2KOu*Xjs6n#AYM4u~at=mW^&*R-NACQ)8nV zvvZ1%OPNc>h>kr;idIRWq?o7J)!(IJpInbbf($9_`2BVWLv7la8ZSxg`Pe%S&gyZW zKNYVzw+^0`q6**5<^ra2%e)jgS=oiEGSPCW00!=Bs*n277Teh-+?u;FZ_t! {JD$qI2*E4vAX!$93}eKcjle8XFJ M?mKR;YsR8`OmygB7@AaS5^tN=;o?~qDBAKH?< zcD3tfJ98z!u&RdhC8@(Wh7y04Nlu$SFK{J-Z^It vf;Z*JpeAV?lUNDGHlxyK+>A$L%1# zY4H{jM6Fn$3wD@RYpneHoamnvP-YnZldpOA3mY89`p3Bw=><3FGn&ooIcEh{ %a5b-smmganDK|*wv9((q%CtjV@B2`+ z&?DC2?dbVys7!@}S^x4!u9P5s }SvI7(pwV|#;r_|HLG)5N4cdwcAHNNLSF ztifee%yNWr1}IRXXAT|}Ms>9@rDn5Fb3q-0+0yo*%Lbdnkn0{uJvd1U(slmWu)+L( zXVZc}4eMcW4CZUxSt@@d^w8+`_}nL;^ J!kYmeMUq0dCc#N#bzgT$82Kc@4;QC)$E0M8f*s$9cDO+0!=-Qt=8Y6d&Ny ze^5t(=A?ImQVdaXliisyZh|Zll0vPgWSS+zCt$xzt$v^j@|<#s=q_T_R3*e!li)XWqs81QB&8R}%%ww-#%LV*yYWA1F^NM<>+qxh@Bjc=7Ejsu-0C*>vj+KR) z#NE_3gnV7DWusmtFi|*|x=>7Nj4-CIrWYUvHI8lW!~_qY92PN`Jt-;d^BJXrXp@0K z-rn4;4fpHSdZBc~A=zyThZ87i3N!G;fC~*P4|)VBFc3h*XncJ)Q)q_OY>%p&hp+kU zzHe4~b(oSfOyZS8UE;g9HJ_AdJtL*|u9Nnzlzr@)>NQ0~M~EmJ+%!&!N%=M9a1@ua z7M3#YA?OD7j(#0op&PGkQYb$ A zVw+FuZ^~5kWGDX|h!k;icMowG53(maP}tAQR8+~bA+y?;+@DL*`1Bv;Z!@F1rcs)% z#6-@w8)Q4hNV7KzmFk8gGGdcJrAt+Jdv%2`y7a~NY%U9F3y gTR#*TLHd5uQpG~i~Ygj zR*arpS^E;9o=hv?<$Cn8_OP(U`O1Rm=ECd%m8k(3rz<_v4eH-Ko+Dma^KoRnsH*dd zb5|MJp{Z)DwyfcER5!==O5q5?_uc8U-oa~5fv|&pU)9mn)J;R `h3EKxup-cDi|Ix%yIbgHh0L`Twsjs%Px*VGe zM78?OIxFAwXHAmm%=lJvwZv$9myuGtE $-=4{kWQ9Djq_c-}U`-vnBL!#<(b zAt}bqQl#?14`GJAd_|whxdThew;pTRK~t{Dc<_g Lq2O@X`yEjQt%a^6R5(M zKE6kdzQt3__j*%dQajL286Wb~97v%lEb~2p#7rOMR%{;-xtK@Sp4)Xo!u>TK<75cJ z(SN3nD53wJt7-?FzpAv8FE&b7r00$<5m)6Y%7h<~eI}qq|A<~X8YX3r yy z%EVD{6kyH{nMbVbrNA+8Kt9!wiOD7N*+y${X(7P+yM}FgyWaGzFy$n9@s_)FV`U@& z?DA9Ceq!ntfw1hQ9KA0u_wtHc>L>E+z0lGC6y>%Xa5bmnzSgaLCpx6)P0K}{%RMU6 zbhiI`tZ8w)G7Nf!zz2I#cmswf5lvG;-i|4w=pcdN1o&2J;xVGSOtU9*c)BG0P!UMP zV?Esb+>ZVQ9XWO|pWG4@^C8wiN)G!dto6h<1Yj82So7X*fLA;^9~-A!-<8LyiNPt8 z+tmcAo%Ia~QneZfS *cVM4yyF5Jt>~Rb?gCTl&>tTnU3t`CleKMvfZXJBT?6R zIA1-#hth!CdyvG6DT;})Bhh3vYVR_$!?>G+SQ)I#w&8c}#I7_&;^8;S9Mh$N&nkHj z1vM_N{#|{i_?gSy5yBi6UL5wxW$s`s{t TOj4kb>AoDQA0@a4J^KPYsS@g zZG9#`