From 0965cd3c7e90b9d671092839cd5a03bdef83faaa Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Wed, 7 Jan 2026 13:11:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E6=A8=AA=E5=B1=8F?= =?UTF-8?q?=E9=94=AE=E7=9B=98=E4=B8=8D=E5=B1=85=E4=B8=AD=E4=B8=BA=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CustomKeyboard/KeyboardViewController.m | 143 +++++++++++++++++++----- KBMaiPointEventTable.xlsx | Bin 22106 -> 22613 bytes Shared/KBConfig.h | 3 +- 3 files changed, 116 insertions(+), 30 deletions(-) diff --git a/CustomKeyboard/KeyboardViewController.m b/CustomKeyboard/KeyboardViewController.m index 20b6069..742350d 100644 --- a/CustomKeyboard/KeyboardViewController.m +++ b/CustomKeyboard/KeyboardViewController.m @@ -47,6 +47,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, @interface KeyboardViewController () @property (nonatomic, strong) UIButton *nextKeyboardButton; // 系统“下一个键盘”按钮(可选) +@property (nonatomic, strong) UIView *contentView; @property (nonatomic, strong) KBKeyBoardMainView *keyBoardMainView; // 功能面板视图(点击工具栏第0个时显示) @property (nonatomic, strong) KBFunctionView *functionView; // 功能面板视图(点击工具栏第0个时显示) @property (nonatomic, strong) KBSettingView *settingView; // 设置页 @@ -55,6 +56,12 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, @property (nonatomic, strong) KBSuggestionEngine *suggestionEngine; @property (nonatomic, copy) NSString *currentWord; @property (nonatomic, assign) BOOL suppressSuggestions; +@property (nonatomic, strong) MASConstraint *contentWidthConstraint; +@property (nonatomic, strong) MASConstraint *contentHeightConstraint; +@property (nonatomic, strong) NSLayoutConstraint *kb_heightConstraint; +@property (nonatomic, strong) NSLayoutConstraint *kb_widthConstraint; +@property (nonatomic, assign) CGFloat kb_lastPortraitWidth; +@property (nonatomic, assign) CGFloat kb_lastKeyboardHeight; @end @implementation KeyboardViewController @@ -119,13 +126,16 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, - (void)setupUI { self.view.translatesAutoresizingMaskIntoConstraints = NO; - // 按屏幕宽度对设计值做等比缩放,避免在不同机型上键盘整体高度失真导致皮肤被压缩/拉伸 - CGFloat keyboardHeight = KBFit(kKBKeyboardBaseHeight); - CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width; + // 按“短边”宽度等比缩放,横屏保持竖屏布局比例 + CGFloat portraitWidth = [self kb_portraitWidth]; + CGFloat keyboardHeight = [self kb_keyboardHeightForWidth:portraitWidth]; + CGFloat screenWidth = CGRectGetWidth([UIScreen mainScreen].bounds); CGFloat outerVerticalInset = KBFit(4.0f); NSLayoutConstraint *h = [self.view.heightAnchor constraintEqualToConstant:keyboardHeight]; NSLayoutConstraint *w = [self.view.widthAnchor constraintEqualToConstant:screenWidth]; + self.kb_heightConstraint = h; + self.kb_widthConstraint = w; h.priority = UILayoutPriorityRequired; w.priority = UILayoutPriorityRequired; @@ -137,25 +147,30 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, iv.allowsSelfSizing = NO; } } - // 背景图铺底 - [self.view addSubview:self.bgImageView]; + // 内容容器:横屏时保持竖屏宽度,居中显示 + [self.view addSubview:self.contentView]; + [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.equalTo(self.view); + make.bottom.equalTo(self.view); + self.contentWidthConstraint = make.width.mas_equalTo(portraitWidth); + self.contentHeightConstraint = make.height.mas_equalTo(keyboardHeight); + }]; + + // 背景图铺底(仅在内容容器内) + [self.contentView addSubview:self.bgImageView]; [self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) { - make.edges.equalTo(self.view); + make.edges.equalTo(self.contentView); }]; // 预置功能面板(默认隐藏),与键盘区域共享相同布局 self.functionView.hidden = YES; - [self.view addSubview:self.functionView]; + [self.contentView addSubview:self.functionView]; [self.functionView mas_makeConstraints:^(MASConstraintMaker *make) { - make.left.right.equalTo(self.view); - make.top.equalTo(self.view).offset(0); - make.bottom.equalTo(self.view).offset(0); + make.edges.equalTo(self.contentView); }]; - [self.view addSubview:self.keyBoardMainView]; + [self.contentView addSubview:self.keyBoardMainView]; [self.keyBoardMainView mas_makeConstraints:^(MASConstraintMaker *make) { - make.left.right.equalTo(self.view); - make.top.equalTo(self.view).offset(0); - make.bottom.equalTo(self.view.mas_bottom).offset(-0); + make.edges.equalTo(self.contentView); }]; } @@ -291,9 +306,9 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, // 可选:把当前显示的视图置顶,避免层级遮挡 if (show) { - [self.view bringSubviewToFront:self.functionView]; + [self.contentView bringSubviewToFront:self.functionView]; } else { - [self.view bringSubviewToFront:self.keyBoardMainView]; + [self.contentView bringSubviewToFront:self.keyBoardMainView]; } } @@ -307,19 +322,19 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, // if (!self.settingView) { self.settingView = [[KBSettingView alloc] init]; self.settingView.hidden = YES; - [self.view addSubview:self.settingView]; + [self.contentView addSubview:self.settingView]; [self.settingView mas_makeConstraints:^(MASConstraintMaker *make) { // 与键盘主视图完全等同的区域,保证高度、宽度一致 - make.edges.equalTo(self.keyBoardMainView); + make.edges.equalTo(self.contentView); }]; [self.settingView.backButton addTarget:self action:@selector(onTapSettingsBack) forControlEvents:UIControlEventTouchUpInside]; // } - [self.view bringSubviewToFront:self.settingView]; + [self.contentView bringSubviewToFront:self.settingView]; // 以 keyBoardMainView 的实际宽度为准,避免首次添加时 self.view 宽度尚未计算 - [self.view layoutIfNeeded]; + [self.contentView layoutIfNeeded]; CGFloat w = CGRectGetWidth(self.keyBoardMainView.bounds); - if (w <= 0) { w = CGRectGetWidth(self.view.bounds); } - if (w <= 0) { w = [UIScreen mainScreen].bounds.size.width; } + if (w <= 0) { w = CGRectGetWidth(self.contentView.bounds); } + if (w <= 0) { w = [self kb_portraitWidth]; } self.settingView.transform = CGAffineTransformMakeTranslation(w, 0); self.settingView.hidden = NO; [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ @@ -328,8 +343,8 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, } else { if (!self.settingView || self.settingView.hidden) return; CGFloat w = CGRectGetWidth(self.keyBoardMainView.bounds); - if (w <= 0) { w = CGRectGetWidth(self.view.bounds); } - if (w <= 0) { w = [UIScreen mainScreen].bounds.size.width; } + if (w <= 0) { w = CGRectGetWidth(self.contentView.bounds); } + if (w <= 0) { w = [self kb_portraitWidth]; } [UIView animateWithDuration:0.22 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ self.settingView.transform = CGAffineTransformMakeTranslation(w, 0); } completion:^(BOOL finished) { @@ -363,15 +378,15 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, KBKeyboardSubscriptionView *panel = self.subscriptionView; if (!panel.superview) { panel.hidden = YES; - [self.view addSubview:panel]; + [self.contentView addSubview:panel]; [panel mas_makeConstraints:^(MASConstraintMaker *make) { - make.edges.equalTo(self.keyBoardMainView); + make.edges.equalTo(self.contentView); }]; } - [self.view bringSubviewToFront:panel]; + [self.contentView bringSubviewToFront:panel]; panel.hidden = NO; panel.alpha = 0.0; - CGFloat height = CGRectGetHeight(self.view.bounds); + CGFloat height = CGRectGetHeight(self.contentView.bounds); if (height <= 0) { height = 260; } panel.transform = CGAffineTransformMakeTranslation(0, height); [panel refreshProductsIfNeeded]; @@ -384,7 +399,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, - (void)hideSubscriptionPanel { if (!self.subscriptionView || self.subscriptionView.hidden) { return; } CGFloat height = CGRectGetHeight(self.subscriptionView.bounds); - if (height <= 0) { height = CGRectGetHeight(self.view.bounds); } + if (height <= 0) { height = CGRectGetHeight(self.contentView.bounds); } KBKeyboardSubscriptionView *panel = self.subscriptionView; [UIView animateWithDuration:0.22 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ panel.alpha = 0.0; @@ -679,6 +694,21 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, // } } +- (void)viewDidLayoutSubviews { + [super viewDidLayoutSubviews]; + [self kb_updateKeyboardLayoutIfNeeded]; +} + +- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { + [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; + __weak typeof(self) weakSelf = self; + [coordinator animateAlongsideTransition:^(id _Nonnull context) { + [weakSelf kb_updateKeyboardLayoutIfNeeded]; + } completion:^(__unused id _Nonnull context) { + [weakSelf kb_updateKeyboardLayoutIfNeeded]; + }]; +} + //- (void)kb_tryOpenContainerForLoginIfNeeded { // // 使用与主 App 一致的自定义 Scheme // NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@@//login?src=keyboard", KB_APP_SCHEME]]; @@ -699,6 +729,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, self.bgImageView.image = img; BOOL hasImg = (img != nil); self.view.backgroundColor = hasImg ? [UIColor clearColor] : t.keyboardBackground; + self.contentView.backgroundColor = hasImg ? [UIColor clearColor] : t.keyboardBackground; self.keyBoardMainView.backgroundColor = hasImg ? [UIColor clearColor] : t.keyboardBackground; // 触发键区按主题重绘 if ([self.keyBoardMainView respondsToSelector:@selector(kb_applyTheme)]) { @@ -732,8 +763,62 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center, }]; } +#pragma mark - Layout Helpers + +- (CGFloat)kb_portraitWidth { + CGSize s = [UIScreen mainScreen].bounds.size; + return MIN(s.width, s.height); +} + +- (CGFloat)kb_keyboardHeightForWidth:(CGFloat)width { + if (width <= 0) { width = KB_DESIGN_WIDTH; } + return kKBKeyboardBaseHeight * (width / KB_DESIGN_WIDTH); +} + +- (void)kb_updateKeyboardLayoutIfNeeded { + CGFloat portraitWidth = [self kb_portraitWidth]; + CGFloat keyboardHeight = [self kb_keyboardHeightForWidth:portraitWidth]; + CGFloat containerWidth = CGRectGetWidth(self.view.superview.bounds); + if (containerWidth <= 0) { + containerWidth = CGRectGetWidth(self.view.window.bounds); + } + if (containerWidth <= 0) { + containerWidth = CGRectGetWidth([UIScreen mainScreen].bounds); + } + + BOOL widthChanged = (fabs(self.kb_lastPortraitWidth - portraitWidth) >= 0.5); + BOOL heightChanged = (fabs(self.kb_lastKeyboardHeight - keyboardHeight) >= 0.5); + if (!widthChanged && !heightChanged && containerWidth > 0 && self.kb_widthConstraint.constant == containerWidth) { + return; + } + self.kb_lastPortraitWidth = portraitWidth; + self.kb_lastKeyboardHeight = keyboardHeight; + + if (self.kb_heightConstraint) { + self.kb_heightConstraint.constant = keyboardHeight; + } + if (containerWidth > 0 && self.kb_widthConstraint) { + self.kb_widthConstraint.constant = containerWidth; + } + if (self.contentWidthConstraint) { + [self.contentWidthConstraint setOffset:portraitWidth]; + } + if (self.contentHeightConstraint) { + [self.contentHeightConstraint setOffset:keyboardHeight]; + } + [self.view layoutIfNeeded]; +} + #pragma mark - Lazy +- (UIView *)contentView { + if (!_contentView) { + _contentView = [[UIView alloc] init]; + _contentView.backgroundColor = [UIColor clearColor]; + } + return _contentView; +} + - (UIImageView *)bgImageView { if (!_bgImageView) { _bgImageView = [[UIImageView alloc] init]; diff --git a/KBMaiPointEventTable.xlsx b/KBMaiPointEventTable.xlsx index 785e13bc548758471ffad2f205a1d909da07af54..aab27715b55c172c6f20111188469bddde246123 100644 GIT binary patch delta 9069 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 N$AIS!GvrpO{|DIjKUn|( delta 8564 zcmaKSby!qg_x8};jifllkWvzo0>aQkgVfL^jY^HuDaz0c-CfesDKVfl$RN@np@5XY zH~752@A-c3AK$t5HRtTP?sczwt+n@@efF7|T8za?jM^CtoMrMa*@Eyz3|5$S9~*V} zism0Ol0*JUr-DMe=nR?G{(gpdckBakXdd?D^IHqUcBuj~*+lPy7ZWnm$5~DdWJd}w zniM{;abxjH7ws1-Xjdo)hu4#b8|3fvi8_*Dj9*-s@H4}+BJoxvk9405Q+~qbG!Jch zC>7!P&P?b&^(Sk1%fUyAhceeNTxs@|eq0YV)o>D}JSFOLz}T{q5(nfH!44`&8j#dK z8G>;Ss~Bfs1%;0alf%Q^=y&q@+U_$&f0k6Wo+{D$LP2}5^Ds78ykGT#C)flfM_RJE z$kLYh`aCbkFswzxpjxanQ>LVlyMpnS))^S-sNqjep`VgcSL66}pP$8$#{`ap`?s8sUVA#Oeva2*E6Z{OoUNAh0CqE z&&~DbafyN8j7u|>VgPyG&g_lWo*zSmc@ve`A?891E-afGGV`pjYB9pR0($~a&ph0d zdb~FyHC|4|xZDLgoX;)t^Fc!pKcft;ZvJ=1-9taSCFX*T4Wofkn!~&~8yPrd&L9^j zi~7WF#i+(7En2BzTjsY#Sua}g>9~Ss7fjfowa(QRXZs}B&+}SS>p`bilLvKdd2Wc# z!_dzC5Lo3!DO6bghXD1Z=ZcewoagxUxO3W@L(tdCUz92oB`Cdo<0~@Pi+9MNmqH(Z zwNnQ@YW?kjv@8r9z_UDVWVII3jV6A&vUYUH#-eOs{h)jE@Tm?leMZ#Op3TYDo5aJB zQRSYqHetfVS5h|3Ywo+OmKW?-g88to2cjQEh+IFoQe~vikV!v z(cKHEX-1jtNK%CfW0`G?{tx3;f_Ul?I^N=FuvU~8-({Ez;YO^JQ+^_E6xb+{G#N_+ai#^*~HM-ClY4Uy8XOj*Jti9wZhF$b<`qw+7pI!%#@e9-xE@-Rxz#^B+hereTP7N>eU>#zz8z1E&y;%%D=*q)p+4(y= zvt_pjxn#bkE(aT3zmPn*BC(rs%Q?RIDxEWe+C9G(h+|9t-LSjUSoy0@xa!M6-}MGT zpui>B*qS6nmin=8N${rSuRks%!RCT(q5ci7IaSVA zS^BsBquKS>ohUV%-zGK7vl+e4TZiWAri@_)cZqwN zwt?5)(?PfI<$if5?prh^-;5QO@QhnlTpy%er*zl_R9X6zj4SMV%YCK0Xe~%DJ^Yb0 z8=c&q&SGUCID1whoqTz{^hC7luxrPyy0h;oQ6yHFHUMXp(kkqT;TyR%jMyXWh^qTx zFZQ>>H$gHm4t7HDDGH3#j)k&W_bw-dol?wQ{gGJDn zi*4@qAP%vFCbb9&FUUNP?F7ni{btBr635*J0!}CasBA*jNp1aaYq=HiVRZqv3ABmW zL_-I!1TfkP9%K1Djpm0jwT@^5%fuPMF{ta%$u2$>u z{mvdES34y90Y+}B)oLM-z;|NkR%zFn!+Q502yFKsj5OGl9qkZAMO4DCmWBu1-qj4b zv+L1%YHQz;LkZeVQW;r^|kg^o=CV3ya&K*o2M5^TPBF>exy3aiC2 zq+FuI99?MYt);8e$&}5fTcGz+kb!);ew*HJrtz@2mulyj-;=02xGlgYS$J*xnk>9W zB8pu5zDU|(aob^)O6&5a)RW{^i@#ZXWhVY%fpDQ#W*$**&9hYNqqP74-?iE#HvkY# zyHuZNZZsU>HfSrqTASu;48j8AL1XZsFUpPi>^bDk-7p)49f_iTTd@P1#NZgfeyAa| zjF|g=6c0xf&jT2317&VuKZM~!k&<-%JIbZfXdN)0JuoFk2TZO9CU3VHfIj5K*mb5V zrEyY^{_TKfaihvhG9Cl-(c~qu72h><=XD~by7D3eBxIhM zmp0afK*uOkzDmiB=QE1Gu)oQNQuodeECsB@qo8ru>e7+{K(u29WDgSu;fwN9uc@GD zR+J1TCs5vQTkm_Vc68r?@k1*}$n*@Syxn|aqH24KJb2fxpuYrLh_?rtL&;4m_axA7 z{;pe>TAV`TY&ZX_{lOO%!NUIEuY zUA3YUut`9o$PsH2U8LBc!=3W}*8ICrQU7x!__b74^imr?aPc5KXK@k(oEb*$^5l2}}Vmorr#k5GcgH{76vrw|JApjh*ZK zK&rbSq6aFmOQ+IXc$cfYaqS`Kk|^&mtEmabzVhu=}t1sKYI??->;hWX}DUj$(bRMo&fTVihADed( znsjA)x4Y#n?42@a?u`nRoB*$6_$~Z~>Ki7+{pS_j%Qdw@din6 z|JpEInH)I@PgBr_y@YSgTs^uZok$)?9k@)+-lVCGyv5K_r~Q9#jr~pOep>_pK)(3h ztr6}+P6f;9mbB|MRa4ocmwpx}YPvrprimbR?&h1!N{4mfJvq8EG$?FM-#J%fUP`dc z{3W@1Gbh)SiJG|dc=o70GPvd5?|{?iGaoBotgu|(-WXksepr#tPH9iwC|;N$yO!It zxt$y8y4}C&kPMXToH;E^RhegIy>u^`x*4_dc-8@%S=#tny)f6VWRcnD_X;e>(n<36 z>PbhSQ*2YF0u$*_=)PCSo~6pU*Ez3^Xid$Yfw^b}>yrPAHe#>RN27XDH)~7&us`(s z38`J8s8Q>SY~<JvXvvdphg) z>iotAcB{_#GDquR&%Z(TdLjE@2xF$?IpXZ>to`iz=kvyP794y+xI&QsrRQU@CFm>D z->ipAyhm5oM=x*dwP+_&J71JqvR>ZeThgYVP&vXjAm!Ib`-4Q<YEz|he3El!@c#4od2Uv!=qRrMmA22Y~Pq>3?}iZ#Y42CZN8?>|>Brb4-ei%w;b z53VMu(`mG8yyNy%E~?5Z%|&u?NloWR;rcEU6{=A8(oZj>Xw1x1ykpeGx6yg}(Q47$ zlrs4Mvel$+rjXS3K~k3^#!-i^Q=YMS1UA6rM7%qas?pB7f?KWVtik@_{e8yvXYb11 zyMWyGHI!*$5v326ML~2p5EnwHIFJ}nk0y?=>|yw1#(hS)Pt)^a$xR_VSv>iJ)SpX5 z!hOoWFpw5$CdXG(zff%9ez z*i2g2zxi({#ZXFGw(j}9INPfbyu82EQ#VN{l zu84~n0;vFLhCEZlTt;daK~HuIP==FDRVGdN1znm}sp&Q%TMM-)U>Mh%@YTRGkIi%pt6V(>{vXA!==UJ??01ow&|7 zLKNA?X1+u`xID9tC`y@wd8=d{X#jy_VO1i%BFRZ1<)PUfX;g1TXbK^#0z`ANSZ8io z?2Xr5jO|xnIUGL2FeKa$7H87IVC1}n&0{?^9(+8RN8((3_M0ZwG|4fwo+|n$Hr9C_ zbTob`{!lIl3NyRU5U*a{HLhM^eKcAc`EmuM3N#q49qk1=gu$vrn3gu{wc!x5ubmj z^j|nI4Z%(-#pBKREaVe|VQgmuZ+R24cHT3>*vb&$ow5j>VDKBI)IgF;Bgu)z;+l22 z??Z=R4A9T;HK0N44R#qiVG>Pi#C6?&B&5~jYqp55tfP0~f`7?zHiv8eS}rXpFE%UD z^u^Trs@P=Spq1M%pp^%rm6OM2J4|puhtQNn9XJM&8ZrZ0VK|&iW@|;go8DA+iS+eC ztLe?kDjJ;kj<~Mh=7F6(zVC-%r`6@*FJ1H;oY{3UhBQW@^_w}UqEmpgiTam3=pS;5 zQ&B!NR%Pb$hNsL_7%o!zPfCdN8ycqR1CX_rv@(%7Py64B^yioSBr5kqcs71wuz`#8 zd(7^~Uu(4cA*}1V^+FNRw=5noRA2xZxlD2nRTC0FHhWYMT~0e$E3@<9tLygRlvdBG*@|O?2AQ_9-6f+V zQAfvjvSHJ^H{Xa79IlDAId|v_9P4LsDrE9WDb}dq6YYIb$nk-p1B}f zOqUnXBg=T8_P%S@>O49Dn7ky2|4B;6mv=;eivuAnC-p}KYR!LQAcLVgo;DBuJ5m@z zvx<;&-YO(@7K6h$b$6oytQ261V}Q5KR3o|Oe43#HY20p%K$kzlX6Y0FqBV}OLdScG z(0x)#2l7(*A@PK-mfF)d;%?xbm@27V(RR%1P&6SuT)7;grdEhoZjkW*`1q+W19SBL z|Cyj;ep7_+mm+03!64S}pLd_`Z&{5%gy^zbH(rM-m(yNz8QuK^3`RuH|8^EAHMU|1 zL)hH}biX>@57j*ZscOxmE9g&C$i{T?YMfxS(uHs77(`>3&>mCEas^8-4a%mVHO95R zE1FjdItn8Nuim#Jl}NOy!VLPNX+oqN8F9HBU6*kY%;|in*~`FJSKBas;g76kM~<=n zbhw+WA|3q@bkWEvC08|Sx|0Yi=ndoIMIH+eF?=VsU;dv#>~PsRF*Rq?W;W*;Au+`( zgab2>MwXhQ4fL~i(2oXno)I?a>FH40=I!Zsaf#e$7rWYMzHHuDMK5`Nxb4oJu-!vx zLIPg21m4^F)mOIAY}s|Oi1kEti*e3m!l3C&cj(QG*x~3m~Vxp0-}Lz+~|n6PyRIwfsUt zt%08RjaH)j&p|{E-`n&`3STMvhliiX;ZD>)wx-b50#@h?-ZxgDSr<%r#V_bwvr}>c zQMgxnnwXFRorWt+5LN%;Jy!DgBx1+)(dTlyx+2hnMJLP73+U5VnHZC*7lhX3GmTT{EK+0gWI>IkyRaJLzKrhei!=k2<#wpfPQt zI|?cXBtNV^&uma}9x`0|<$$y_J(3}bUUIGr0ajhmhS!KJ)#f{fTRGpr9GgOrxuvX@ z(`(OFv%`|>o??lS*0xX@{TijU z^Vle@Uco%Qp6e9plPN-tA>@7DZ_({P9p!yKK%qWGk>uiQ_l-DP~w4Y;peNeXYgJFQDoC{IOB1rgGBy+r6xKA+d~R*np3#g*#4u#1W~& ziX)0;s!nb=W=E5kt=z;~)cqggG|}E^M-=Vn?w2i4B~4y(;<(Ko(DW|BU_b!xW%BId z`wW(OZ4l`zU{#3Sw-A?!Zne+ur8i1H+swo!p7jVEkR}|-_FK)hh(AMkRts>|3J1`> z=Xk?|X}=yR||Nl#K~Sgo3=)NQaf zW%rg0671dgI_G>o_)l+3=k28l3ny#a^O)W9Z|8aw>Dx9IZn0~7p~rJMJE`F!PK!TU z#z|*%!aP`>{I1c~0xN+$XYZp(1XXe%55uFpt8-&akT^r&DK@ zivOTUf_gt8xOI+TXLedLXJYEOEPZomln<81CgE`TLya5^i~lE+!Yi;|m6 z9{bgFKB;}_@zNvv&ugy}cRBD`c7%t!&C%K1&o8o%R`xy_9@d{dxzcC8;c42>9z2B= zH-u+gtsa#l_BgtXwi*R2&iG|Sas=8Q{@Rtt+3|(Ynt#kvOAjK|V~q*_d}3P4`xHJF zpf^`1pd4|~p#I|W?6HqgkI$y^u|&(qx4thT)lMq*#lQXRtVU81a*Y>D+ASLmb;W*q zh7t=dFf^5ak|W0wdXBnglfJ+akVL$K;pYG@@u^;8^g594xI~L8g9vNh#f-RUIk3~B z0Em@b_2B1;ZS(VqrKg-~WeZg@c@G!NGQ%s*cZctxYjD}FRzNmAIX?&ULf9PqVdAQ{H z_2fC;XHb{n4-%Hr`;iCxnuZZ~(2CGqqk~hK%jKK%(UBT{_HSey&!zBx*_R>UxOG#vS z9hed%)+A2)`>Aql?4;3Gl?e7GufaOXCpFgFlOhUj5^GscCX{fpK1=$IG034JvQE6mUPpoZ zhez?H0Wx2uNLUwlr&snr>hqhop1Pm;?3e9$4P}1mYcF@EsA1iXakg?=nO}}sJ}_Xt z#d^omAo_rx8>e&f^ra%)UPF|srFtp5SH*Ne{Xx~d`BQEYtJtZ`2EB-uqZOd46cYze+*egdxcmBzK`BN!5w*l{0fuYx$Fq?!sF1SS@CA~k=r zsvb#7Elw~_@N?a*qcR2H>Xz}<;Y(CIGbPhcb(kxNf`Ex}dB9eb7SdZ@4yJg|Km3p- z_X(Chlrv3%i&8aZT|cC-b0w#f&$27Ybm-TQVM2c;uoDO!E$8UUx{PRshk`)(*8_v=xS))aLHTh@O@E_B(KYLIjY~2sg*66S= z-|#QfsS~Zx`Mm4gBMPfX4nft8TryWREYDPx>?LOo#vLSg97m6HmsE2vPDk#Hm@imV z;ZljJKzr8<9L4mSRDLuXqT;7CnJcVslXE{G_E%L--V%M}Vl9gF#x?6hyjcq9P!M$Z z@%)9yQTNEbuLtQ_emCcST`R$&+p^C}Ue6H{Zj5bsn`*OE-#)>tl56PJiowQwsSQui zrGXb2GQorNNHGUA;PHCG=xr8wk6z+`{y1Q)ua5zQSLw@Rz~Be^LYNd@FA`0&or-e77FEzN~s|J#o4ENzohGLlV_u+bmK&HRL5xuS*#RdS#(60sW z4h#Sr`qK~$PcoFo$c0ZDvNQerrX2vFzr&!9e_&(SaG`rNi8wfLE+cZLf8QtmH_jCQ z4hMHMqG9@XXZ6290c3wc)o9S)75Co-_kV-Z7|