From 74e63d31e23bb22af06489cc82b7f7805f7e1095 Mon Sep 17 00:00:00 2001 From: Krystian Sienkiewicz Date: Wed, 5 Aug 2026 12:23:47 +0200 Subject: [PATCH 1/5] fix(ios): apply obliqueness if italics are not supported --- .../InputAttributesManager.mm | 30 ++- ios/interfaces/StyleBase.h | 1 + ios/interfaces/StyleBase.mm | 5 + ios/styles/ItalicStyle.mm | 26 +-- ios/textHtmlParser/TextHtmlParser.mm | 14 +- ios/utils/ItalicUtils.h | 8 + ios/utils/ItalicUtils.mm | 197 ++++++++++++++++++ 7 files changed, 255 insertions(+), 26 deletions(-) create mode 100644 ios/utils/ItalicUtils.h create mode 100644 ios/utils/ItalicUtils.mm diff --git a/ios/inputAttributesManager/InputAttributesManager.mm b/ios/inputAttributesManager/InputAttributesManager.mm index fc07d871f..664327ceb 100644 --- a/ios/inputAttributesManager/InputAttributesManager.mm +++ b/ios/inputAttributesManager/InputAttributesManager.mm @@ -61,6 +61,14 @@ - (void)clearRemovedTypingAttributes { [_removedTypingAttributes removeAllObjects]; } +// Paragraph styles always go first, inline ones are ordered by their +// stylingPriority +- (NSInteger)stylingOrderFor:(StyleBase *)style { + if (style == nullptr) + return NSIntegerMax; + return [style isParagraph] ? NSIntegerMin : [style stylingPriority]; +} + - (void)handleDirtyRangesStyling { // Filter out 0 length ranges for styling. NSPredicate *predicate = [NSPredicate @@ -97,16 +105,20 @@ - (void)handleDirtyRangesStyling { // Sort style types so paragraph styles come first. Their broad visual // attributes (e.g. foreground color, font) are laid down before inline - // styles override them on their specific sub-ranges. + // styles override them on their specific sub-ranges. Inline styles among + // themselves follow their stylingPriority. NSArray *sortedStyleTypes = [presentStyles.allKeys - sortedArrayUsingComparator:^NSComparisonResult(NSNumber *a, - NSNumber *b) { - BOOL aPara = [_input->stylesDict[a] isParagraph]; - BOOL bPara = [_input->stylesDict[b] isParagraph]; - if (aPara == bPara) - return NSOrderedSame; - return aPara ? NSOrderedAscending : NSOrderedDescending; - }]; + sortedArrayWithOptions:NSSortStable + usingComparator:^NSComparisonResult(NSNumber *a, NSNumber *b) { + NSInteger aOrder = + [self stylingOrderFor:_input->stylesDict[a]]; + NSInteger bOrder = + [self stylingOrderFor:_input->stylesDict[b]]; + if (aOrder == bOrder) + return NSOrderedSame; + return aOrder < bOrder ? NSOrderedAscending + : NSOrderedDescending; + }]; // re-apply meta-attributes and apply visual styling following the saved // occurences. diff --git a/ios/interfaces/StyleBase.h b/ios/interfaces/StyleBase.h index 41a3d27de..b59e75308 100644 --- a/ios/interfaces/StyleBase.h +++ b/ios/interfaces/StyleBase.h @@ -14,6 +14,7 @@ - (BOOL)isParagraph; - (BOOL)needsZWS; - (BOOL)appliesStylingToTyping; +- (NSInteger)stylingPriority; - (instancetype)initWithHost:(id)host; - (NSRange)actualUsedRange:(NSRange)range; - (void)toggle:(NSRange)range; diff --git a/ios/interfaces/StyleBase.mm b/ios/interfaces/StyleBase.mm index f43c53224..c1374c1b6 100644 --- a/ios/interfaces/StyleBase.mm +++ b/ios/interfaces/StyleBase.mm @@ -45,6 +45,11 @@ - (BOOL)appliesStylingToTyping { return NO; } +// determines the order of which the styles are applied +- (NSInteger)stylingPriority { + return 0; +} + - (instancetype)initWithHost:(id)host { self = [super init]; _host = host; diff --git a/ios/styles/ItalicStyle.mm b/ios/styles/ItalicStyle.mm index f2a161141..873bb6039 100644 --- a/ios/styles/ItalicStyle.mm +++ b/ios/styles/ItalicStyle.mm @@ -1,5 +1,5 @@ #import "EnrichedTextInputView.h" -#import "FontExtension.h" +#import "ItalicUtils.h" #import "StyleHeaders.h" @implementation ItalicStyle : StyleBase @@ -17,21 +17,15 @@ - (BOOL)isParagraph { } - (void)applyStyling:(NSRange)range { - [self.host.textView.textStorage - enumerateAttribute:NSFontAttributeName - inRange:range - options:0 - usingBlock:^(id _Nullable value, NSRange range, - BOOL *_Nonnull stop) { - UIFont *font = (UIFont *)value; - if (font != nullptr) { - UIFont *newFont = [font setItalic]; - [self.host.textView.textStorage - addAttribute:NSFontAttributeName - value:newFont - range:range]; - } - }]; + [ItalicUtils applyItalicInTextStorage:self.host.textView.textStorage + inRange:range]; +} + +// some styles might apply a new font (inline code), so we need to apply +// the italic last, that way knowing if the used font supports italics +// or we need to apply a slant +- (NSInteger)stylingPriority { + return 1; } @end diff --git a/ios/textHtmlParser/TextHtmlParser.mm b/ios/textHtmlParser/TextHtmlParser.mm index 3899fd2d8..8f388b943 100644 --- a/ios/textHtmlParser/TextHtmlParser.mm +++ b/ios/textHtmlParser/TextHtmlParser.mm @@ -153,8 +153,20 @@ - (void)applyProcessedStyles:(NSArray *_Nonnull)processedStyles { } } + // Respect the styling priority + NSArray *sortedInlineApply = [pendingInlineApply + sortedArrayWithOptions:NSSortStable + usingComparator:^NSComparisonResult(NSArray *a, NSArray *b) { + NSInteger aPriority = [((StyleBase *)a[0]) stylingPriority]; + NSInteger bPriority = [((StyleBase *)b[0]) stylingPriority]; + if (aPriority == bPriority) + return NSOrderedSame; + return aPriority < bPriority ? NSOrderedAscending + : NSOrderedDescending; + }]; + // Apply visual styling for inline styles - for (NSArray *entry in pendingInlineApply) { + for (NSArray *entry in sortedInlineApply) { StyleBase *style = entry[0]; NSRange adjustedStyleRange = [((NSValue *)entry[1]) rangeValue]; [style applyStyling:adjustedStyleRange]; diff --git a/ios/utils/ItalicUtils.h b/ios/utils/ItalicUtils.h new file mode 100644 index 000000000..252e1b2d5 --- /dev/null +++ b/ios/utils/ItalicUtils.h @@ -0,0 +1,8 @@ +#import +#pragma once + +@interface ItalicUtils : NSObject + ++ (void)applyItalicInTextStorage:(NSTextStorage *)textStorage + inRange:(NSRange)range; +@end diff --git a/ios/utils/ItalicUtils.mm b/ios/utils/ItalicUtils.mm new file mode 100644 index 000000000..5340392b8 --- /dev/null +++ b/ios/utils/ItalicUtils.mm @@ -0,0 +1,197 @@ +#import "ItalicUtils.h" +#import "FontExtension.h" +#import + +// slant used when a font has no italic face +static const CGFloat kObliquenessFallback = 0.2; + +typedef NS_ENUM(NSInteger, ItalicKind) { + // whitespace and other invisible characters - takes over the kind of its + // neighbours + ItalicKindNeutral, + // character must not be slanted at all (text attachments) + ItalicKindNone, + // font has a real italic glyph for the character + ItalicKindFont, + // no italic glyph available, the slant has to be used + ItalicKindOblique, +}; + +static NSCharacterSet *NeutralCharacters(void) { + static NSCharacterSet *neutral = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSMutableCharacterSet *set = + [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy]; + [set formUnionWithCharacterSet:[NSCharacterSet controlCharacterSet]]; + // ZWS + [set addCharactersInString:[NSString + stringWithFormat:@"%C", (unichar)0x200B]]; + neutral = [set copy]; + }); + return neutral; +} + +// returns YES when the font renders the given UTF-16 sequence itself +static BOOL FontCoversCharacters(UIFont *font, const unichar *chars, + CFIndex count) { + if (font == nullptr) { + return NO; + } + CGGlyph glyphs[2] = {0, 0}; + return CTFontGetGlyphsForCharacters((__bridge CTFontRef)font, chars, glyphs, + count); +} + +@implementation ItalicUtils + ++ (void)applyItalicInTextStorage:(NSTextStorage *)textStorage + inRange:(NSRange)range { + if (textStorage == nullptr || range.length == 0 || + NSMaxRange(range) > textStorage.length) { + return; + } + + // we process each present font + [textStorage enumerateAttribute:NSFontAttributeName + inRange:range + options:0 + usingBlock:^(id _Nullable value, NSRange fontRange, + BOOL *_Nonnull stop) { + UIFont *font = (UIFont *)value; + if (font == nullptr) { + return; + } + [self applyItalicForFont:font + inTextStorage:textStorage + inRange:fontRange]; + }]; +} + ++ (void)applyItalicForFont:(UIFont *)font + inTextStorage:(NSTextStorage *)textStorage + inRange:(NSRange)range { + UIFont *italicFont = [font setItalic]; + BOOL hasItalicFace = [italicFont isItalic]; + + NSMutableArray *clusterRanges = [NSMutableArray array]; + NSMutableArray *clusterKinds = [NSMutableArray array]; + + // we process each composed character sequence and classify it to a specific + // ItalicKind + [textStorage.string + enumerateSubstringsInRange:range + options:NSStringEnumerationByComposedCharacterSequences + usingBlock:^(NSString *_Nullable cluster, + NSRange clusterRange, NSRange _, + BOOL *_Nonnull stop) { + if (cluster.length == 0) { + return; + } + [clusterRanges + addObject:[NSValue valueWithRange:clusterRange]]; + [clusterKinds + addObject:@([self kindForCluster:cluster + font:font + italicFont:italicFont + hasItalicFace:hasItalicFace])]; + }]; + + [self resolveNeutralKinds:clusterKinds]; + + // merge neighbouring clusters of the same kind and apply the style + NSUInteger index = 0; + while (index < clusterKinds.count) { + NSUInteger endIndex = index + 1; + ItalicKind kind = (ItalicKind)[clusterKinds[index] integerValue]; + while (endIndex < clusterKinds.count && + (ItalicKind)[clusterKinds[endIndex] integerValue] == kind) { + endIndex += 1; + } + + NSRange startRange = [clusterRanges[index] rangeValue]; + NSRange endRange = [clusterRanges[endIndex - 1] rangeValue]; + NSRange segment = NSMakeRange(startRange.location, + NSMaxRange(endRange) - startRange.location); + + [self applyKind:kind + toSegment:segment + inTextStorage:textStorage + withItalicFont:italicFont]; + + index = endIndex; + } +} + ++ (ItalicKind)kindForCluster:(NSString *)cluster + font:(UIFont *)font + italicFont:(UIFont *)italicFont + hasItalicFace:(BOOL)hasItalicFace { + if ([cluster rangeOfCharacterFromSet:[NeutralCharacters() invertedSet]] + .location == NSNotFound) { + return ItalicKindNeutral; + } + + // we just need to analyze the first unicode character to classify the whole + // cluster + unichar chars[2] = {0, 0}; + CFIndex count = 1; + chars[0] = [cluster characterAtIndex:0]; + if (CFStringIsSurrogateHighCharacter(chars[0]) && cluster.length > 1) { + chars[1] = [cluster characterAtIndex:1]; + count = 2; + } + + if (chars[0] == (unichar)NSAttachmentCharacter) { + return ItalicKindNone; + } + + BOOL coveredByFont = FontCoversCharacters(font, chars, count); + + // italic style is supported - we use it + if (coveredByFont && hasItalicFace && + FontCoversCharacters(italicFont, chars, count)) { + return ItalicKindFont; + } + + // italic is not supported, we use the slate instead + return ItalicKindOblique; +} + +// neutral clusters take over the preceding kind ++ (void)resolveNeutralKinds:(NSMutableArray *)kinds { + ItalicKind previous = ItalicKindNeutral; + for (NSUInteger i = 0; i < kinds.count; i += 1) { + ItalicKind kind = (ItalicKind)[kinds[i] integerValue]; + if (kind == ItalicKindNeutral) { + kinds[i] = @(previous); + } else { + previous = kind; + } + } +} + ++ (void)applyKind:(ItalicKind)kind + toSegment:(NSRange)segment + inTextStorage:(NSTextStorage *)textStorage + withItalicFont:(UIFont *)italicFont { + switch (kind) { + case ItalicKindFont: + [textStorage addAttribute:NSFontAttributeName + value:italicFont + range:segment]; + [textStorage removeAttribute:NSObliquenessAttributeName range:segment]; + break; + case ItalicKindOblique: + [textStorage addAttribute:NSObliquenessAttributeName + value:@(kObliquenessFallback) + range:segment]; + break; + case ItalicKindNone: + case ItalicKindNeutral: + [textStorage removeAttribute:NSObliquenessAttributeName range:segment]; + break; + } +} + +@end From bb2e588ef7de8a842114e4b2c6c771c166a55886 Mon Sep 17 00:00:00 2001 From: Krystian Sienkiewicz Date: Wed, 5 Aug 2026 15:16:59 +0200 Subject: [PATCH 2/5] feat: drop the neutral kind behavior --- .../InputAttributesManager.mm | 5 ++-- ios/interfaces/StyleBase.mm | 2 +- ios/utils/ItalicUtils.mm | 26 +++---------------- 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/ios/inputAttributesManager/InputAttributesManager.mm b/ios/inputAttributesManager/InputAttributesManager.mm index 664327ceb..5d5abb213 100644 --- a/ios/inputAttributesManager/InputAttributesManager.mm +++ b/ios/inputAttributesManager/InputAttributesManager.mm @@ -114,8 +114,9 @@ - (void)handleDirtyRangesStyling { [self stylingOrderFor:_input->stylesDict[a]]; NSInteger bOrder = [self stylingOrderFor:_input->stylesDict[b]]; - if (aOrder == bOrder) - return NSOrderedSame; + if (aOrder == bOrder) { + return [a compare:b]; + } return aOrder < bOrder ? NSOrderedAscending : NSOrderedDescending; }]; diff --git a/ios/interfaces/StyleBase.mm b/ios/interfaces/StyleBase.mm index c1374c1b6..c4ebeed5b 100644 --- a/ios/interfaces/StyleBase.mm +++ b/ios/interfaces/StyleBase.mm @@ -45,7 +45,7 @@ - (BOOL)appliesStylingToTyping { return NO; } -// determines the order of which the styles are applied +// determines the order in which the styles are applied - (NSInteger)stylingPriority { return 0; } diff --git a/ios/utils/ItalicUtils.mm b/ios/utils/ItalicUtils.mm index 5340392b8..5b6ec0bb2 100644 --- a/ios/utils/ItalicUtils.mm +++ b/ios/utils/ItalicUtils.mm @@ -6,10 +6,8 @@ static const CGFloat kObliquenessFallback = 0.2; typedef NS_ENUM(NSInteger, ItalicKind) { - // whitespace and other invisible characters - takes over the kind of its - // neighbours - ItalicKindNeutral, - // character must not be slanted at all (text attachments) + // character must not be slanted at all (whitespace, control characters, + // text attachments) ItalicKindNone, // font has a real italic glyph for the character ItalicKindFont, @@ -97,8 +95,6 @@ + (void)applyItalicForFont:(UIFont *)font hasItalicFace:hasItalicFace])]; }]; - [self resolveNeutralKinds:clusterKinds]; - // merge neighbouring clusters of the same kind and apply the style NSUInteger index = 0; while (index < clusterKinds.count) { @@ -129,7 +125,7 @@ + (ItalicKind)kindForCluster:(NSString *)cluster hasItalicFace:(BOOL)hasItalicFace { if ([cluster rangeOfCharacterFromSet:[NeutralCharacters() invertedSet]] .location == NSNotFound) { - return ItalicKindNeutral; + return ItalicKindNone; } // we just need to analyze the first unicode character to classify the whole @@ -154,23 +150,10 @@ + (ItalicKind)kindForCluster:(NSString *)cluster return ItalicKindFont; } - // italic is not supported, we use the slate instead + // italic is not supported, we use the slant instead return ItalicKindOblique; } -// neutral clusters take over the preceding kind -+ (void)resolveNeutralKinds:(NSMutableArray *)kinds { - ItalicKind previous = ItalicKindNeutral; - for (NSUInteger i = 0; i < kinds.count; i += 1) { - ItalicKind kind = (ItalicKind)[kinds[i] integerValue]; - if (kind == ItalicKindNeutral) { - kinds[i] = @(previous); - } else { - previous = kind; - } - } -} - + (void)applyKind:(ItalicKind)kind toSegment:(NSRange)segment inTextStorage:(NSTextStorage *)textStorage @@ -188,7 +171,6 @@ + (void)applyKind:(ItalicKind)kind range:segment]; break; case ItalicKindNone: - case ItalicKindNeutral: [textStorage removeAttribute:NSObliquenessAttributeName range:segment]; break; } From e443c55704a8a15aae322ea2cb66edf9eb5fa4fc Mon Sep 17 00:00:00 2001 From: Krystian Sienkiewicz Date: Wed, 5 Aug 2026 15:17:15 +0200 Subject: [PATCH 3/5] chore: update e2e baseline screenshots --- .../screenshots/ios/inline_styles_merge.png | Bin 9116 -> 9124 bytes .../screenshots/ios/inline_styles_removal.png | Bin 11203 -> 11224 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/.maestro/enrichedInput/screenshots/ios/inline_styles_merge.png b/.maestro/enrichedInput/screenshots/ios/inline_styles_merge.png index a52dbeef617b442d646b07c1c3c32f842ebee75d..7a978bfacdeaf7086390f133da2c8c8cb934103e 100644 GIT binary patch literal 9124 zcmeHsS5%YR+b5Qzh=_tBpdbnaMI;D_5UPSSkzN8yK!Jet5<)L39+ln%qy?lm6X_62 z6qMdu2vwAt&^v@OJLiACH8X40nv1!ZtB;F>td+d)e)oQ!UweYIHC4_rTw$Q2qdTXj z`bd|K?xZ33KZgD|col4BfY8zL+NnKysPAdII7Urn+B|AneX%Q<$|6= zuRQ8rw3-!PwwsYHK`hU9w)+$w9Rs&~)%}$RywP2_^5{D4{f5Fx+Db0){P)NIue%Yw z#5~s;E68@yb+(ghY%tZbhdfXq?KanaHy<_IS?#ers5HrsDD_&OneWRHHmO`$AeDF* zsnb_)3>8@j8Ww&Mw;SA9{d1OqA^S;!Xu#oqm;6ypZmw$lZ%nbrvMDSfBqU_>&09`M z=U*jVa@F3O`KI9cmN56i9z?$w_ojS#T4rk;l#i%(*VOFH)5~$|i)xl$uE_`%gku+s zIW$Fg>`28~eX!mbm2l1;CCJ}7>d2h6wKZ~oUd9%2<5%GMnVA_!Qn3;)v{b@-<0bX_ zxW|NF!2arcQ_h=b8r|&%3gG9X-wYNS_hqO=WNRdab4hPG?NUzAJxEmTqwlZtqaJbV z@W1%=?REZit^@)5@SwxlVG&lmvSoQWZr`j?!#jsY=hH$Wzv~;DdwWDJI z{m!RW-6Cec4`(J{>l9~zGnIugSTu#lX5O@ze)Hz|nP4sXgHmhVXm0+P9~ZYh(PrC#0IqeH4Kak#>X01A-3qJm4>-Tuc1 z=9wrK5{ZOonxBO#rzJ@^$*s4GxffZ^Ov!W6(e0=;(f1b`i&>BUVi&jV-}q+LkqG(Y zN`R$UYCp@0Ty?NP_y-Jccl zoztcZy}FwVq`rmsS?clR;NFFi3SkKe_}Y1i`mu{k-ZVT6cwHLuz`6^AK6NgIkI`K? zWyAJb?r_J#y8D+Ws5vxZr~Q+N;&up<_3~vhp3kJ#+aTZp!%|r_Jhv&`|8UnD@tVNJ zbAgWTMWj3P%avc>JJY@wIZrBpHJ>=m)DPCI5^-$+tu|EaQ*lVwcKg9p=afeW8RIS9NtBf8}ogEr_R~- zq$^Rk7TN9?rNv#7_cv+0SL?lL+ncEdN-Rm-ZpI@vt+LW{439o>ig6BXd0(~$t%V8x z2MY@e7+Igkc%ve7rO%UXceo;E^~B%5?jDcEIWDP+hhJsxzSg76yO_-9uu%vu_H!M<79`y8tKUG6m zZ2u>MO|9e18M<$XkM7K>EN{eKIyZ0QvFaZfzb_Mx{7aC#rbHzj8U`$*qYBN1rCff$ zWfV?s3FnG_@VK0LcRnZ86@^M6r5^y0QY;=^8msO=BR{UU3gYy0wI9MyUvJPW$@?W^ z=6c2FcF8Zq9$2U9mY5*KVko4Jx+L8-M|n9?zPP(St6t6)!YRkGTXnNr%m8a*%=0o- zr~0H*i9dKie0pKa!}v+uTibP%hHHL%iZmQ&n3$fP?tif7RaY{=pOc&1E5E-IXj;Mt zONDIxc$uS_BGtPHUVH7ly+GkFmSsMi#nCl|b6u5mPMc6^zCjtOa58zlxV1Q9H(2Pk z`jZX)EFfCdzRrZQq@1P}!%zBIoFk&pX4Z#}`j`?)M>mR|XMGJdt1Bc}2o;#tdgGxw zOB|_4`^=DZzL?y{)N?WvB2L2WlEoDn&#$EV{ye>dcf9_m7}RgT;5z7WO>8AKZox|LH@dj2H@o;Uc(&dli7(VM+ zP&Bh|-ivX5Za6tsadj>uLaM-i=<7p&o}+VXg1pxCQM@W{C{w;`Zj?a{+;SEiXSx+W zF-G5CYNMCqKiwMZy_$OX1{;z&^Y$d&b&X8;Nzl>phwf#C_Z^1k@Q+&PABc~qE1gAL zicLt!yN#@=19xSQN4DSSoa@Qh{Qcaj3*%6+GLz&)M!r@%=~~VQjgUXAw9S7QcnY7J z`3phD`Yx6c`EKQNH?A~Zb(&LF$NL}PLy?=dSy9KbCj3;4jg76QGu2|;lsJs*e7(Su zI=b@B2#27A?&$bVHAl(%Z0SGaQCF{(-T+;LBOKzZ;uLw(lP57PsGXI`e6E&LES+e? z#&A^f-ShJMUrjGSf{4Nzq;}8i)xv5&)#YLCGZYVAHE{rkGh5LyilVq zD>zbS$HuFRZ;6C9c06_MPZYPyCI;aFho%M`dYvf(u&H0MNRo1!OG^sbJESfTShd~f z253oySRd?d&ZsnV6uZsqZtGnx7MWRmbMET>P}w*x3CH9fazL5Gh~c|70I?-bM={KV zg|;_;oo$!jlBX_|a*joOFm%V>^(0yU{T(0Z@vL-;uA9Pfx=6WR z!Q)=0GI^k^4DX88CrP@bPxxtwUg_4LKB=g+?z)DURctiSNW3>s#HFt92QoqjE(#lN za0G#Rm*x;);qt@C`8_>#*(P(cuBWEtucl#{RzV=Jhz>w zasUB1ld9f>JzkD;SHlhnlMU|+9-mPH3WeKu<@LhH(royLK4HSXdH74@uWNs9T!1<3 zZOs3nM#7Kf0#!4on>t?(Ntlyr;Ej+G1%wLprB`QdcB5%J&`Kg$1bvW} zDBWUDMibzbgC#EVP*H}hp}C@qOA%JR9(aNQl@Bn~%$$_s3N*I=Gv#n&rQy8O7~WtY zRnn#2_t*EH5^F63oAuG1%R*0g&^X|h6*RDJ#g3$(Y1FQo*YkdsD96*ZHz|nA#d{0(b+(1 zd9(owq&Q7jCQEy0N-UzyBB#a1wmykixFJ4fh65)5`AP8!P33>t?37-fOWzTIIhU~-sziIZdHyh- z?k~?OetV1l&;k%Ad$h7YY=@iIZCm4n%uY?s;cs=})qfGSxyrS$OO>yqGgcB2cO9dVe1ub|pAOAPY$S8Gf--g*hBDR_RX zJ_O8X&)+*<>q7~?h2j*>*#^qS?eBO{0Fri9){b9eTdbaKtDd|)f>rMUdg1|IErXuV zY^t-uNt9nRIX5ZXb!pJK|I0Hto03=E=>{jP^-nIo;c|z9kl$0g*E_zxXaEnzhsce{ zH7JOK-WKqTw~9@cZ@rHu>m#fqKcvG~5-{KWR=@UF{js-=#v|L}<{5;6nFK`X8gg04 z{7J;*99W%>PLw0h<99PO*ww27LJ~3ARfSVy34iMLr}5mnK-vw(ce!s~0jffdU%gD+ zwKCPoGyF2VNg<+@T3l*Qvepj0hdGp5SK@)kg=Iag2dBmRKTpf%Rv3@=ix;2Q7b(r{ z?F%eVh*-pFM)t4ni%-WKKPD~>4(1yOBG|rca1NjHO5|bj69{`+WK!iC9TI*`Hedb3 zy6;q@7w%<3C2BUDTFXPTLNxqGzRcnuDoYs-b1?Vl;pm0uRIR8m_2(*~i{CT2U zh@tvUE-+iXwy8A>iDw6qrN)yvVGmumA~s+x5}sLfcM z3IDA4K3{ZwkgMs}_rUk3(*_xKx<90bunA+ImOYm0kcFhZwcQT2oVj>uAm4y0d#%K} z%Y6{ovT9OcVmt3vpNUBBFD_!Gq5GhT2cScI`jn(UFq_^*5fbB`4aRM#Q)f3h^+l8X zv>v62F5oZG^jzlWd%+L9yu3Q&b1ga&#qrP^w$Ck-V5n^DdKEh>(P4F|sO(wR<5*DP z8$jhAE+q!G3GMJooEfQf$vAxVDe$YR41!GZT!?Rce41=sGLW9%#D!SQx(U2 zdRF1s;+{W$@rt-@=8URQjc1W@c9qx})INJHF%c`)}arBa- z@W)(D1r@HbyH-t*aX&^Q?ypY-%KqikcfGfcm%Z#!2#9|=2xtn8P#D!}59vsfph%^* zR@U_+GpTzN9EIOkA8X#i0o#+o~;na2>^kgwu>ZXjy#uF7H)MxH|=ZseFkK# zb5UBUDJdymLReU-q)bmlov*R1U=~jt71Q#g(5R^1e$Y1!___v^ZRBSZXO ztF63O99)98k2Xy%cG(B&Ecp$egtR^Lt$)z$pZ);5IRMXN{PlZ}*O3tu|4AlQZ`-#a zd-hRRk(#ad&g6TvqMYTU-yU|!v%C_=X07P40Y`^q-nSF#Ru12*H@;Mi_oCwr$oC9^ z5$vUSjuEH)TWri}@z#n9?pHXsylI)O!7Q(3M0-|rWoTCgCU^oQBEZo*i<4e4B*#M$ zqVcD0TG-`8fsXibH7nnsV5V>hrl=O&xN3#tuOht&dGH^ORh(ye4ItavpE^uCCJM^{ z5Pt)ep&GKdT2Ir^$2(Xt4gN{bP|?rV7ud3| z|6Z-%A@y7-HB10)PsV)4dT)~Pw^&@d5K*zQo5sn5U!HkvEf|lv8GBC0!jsS0^=1ZD zy(ASQJ9%1;X3{O2mn1uaU*X(c#3ZzmVJKzzPPNOQcU4&G)=$;e##QyWJ1z``Q}_Ru zO-Ar^6U_n^eW_A-xA}Am3vztIeBFn0!cU~tDSft5HCYlwf9XmgYwPP{D4cN*!N@1P zUY5Hm0U!fbJ)Am_P78tlL0P=n?fm*zNwbV-aBRTqe1L`F7D1%Yt>ghRz+Elt22oMb zb~`^Hl_bigRzH1N7%FP8zdxBXU+yqsReq5uwxpz_G=qdoM?g-KgpV((^u-s%d%#N4n5Vdhnyizq6ruU9}^3Hqcb1e4pHx`>EBxp}El z=;looBF7t#`JW+dXoO9eimz4fdh*BLzg!FdSQ(KSsu{V+Rus`-zSJE}Td*Sk)D`|SqAr=37EsT53a;=XL zFB4ZF?*J#0vxV%|rdsfwv*!8<6X?r9a+7^&4^M19#Ka5Br0SR!TLxj(-&kYjM=Egt z;M#H>Rg-8u_>V48!^AXubw)ZeE!YDNjvdXSEU~Qfe*^hX*+!2FH#*K+?rseGx>70V zVPeP$yP>b2`@*dcH+WwR85aS6>FuHq>b1gYB2LTAmSx<&;0{luS5R?3N{*#c>v)_% zmixc*Xr3Rz6X^?)HmeO!k@g_t-{#KyIbei!Hfz|$EgBgS@XP(4>g`DqT5L)@8OmY0 zNb`-k9z666`H*`Ai0V8FBWOAhXZE1u%7BMJz@umRTrs!;c$fdsc)*b#BL0GS7`lcg z0LD9ST4)-o9LL)%je5fl9JJ$b zaX2R|ox&3ZxPdIy2GW=q?79m;_XFzo>4cw4zy^n15!dA|1^F}r?rIKI0aB}NMbobf z9uGQBqA~QZ@6~R=CE`1aw-!hll*enypF2@^e1MV!n4bIi%(eB|E@Bqc^(&vyou{SE zD5*NoRESgEz;dYBXG_k@qM#?+1cOz(;sxQI0xRlgS9MR{6BKMiB?jx}CA9)&m@QcI z>C7XhAE28u1=GQ!N|>$MKuSsFQ@q-#a(>B!GO3n&$G6nJi9%HWCDpM^rIWXz(E9`r z2mkFZzBGrC@_ZKp$yHWZ`LASsG7!e??d_%MVIB)ZE&w9@i5I3a#?~Lf^YvR$*RdE! zkA9rl5lFFQy*K87`-}iK4YSlWZ#Q`3VFQvMgDg%v z0>-7%I=8L4IkGf^lFq=upkG7_BVrM#h(n|{v?T{zMl*|gxf=OCXG;vE5^SVYKX z-$rhuqe6V%Ns$x~8#x7VoZPgxPoZM?^Af8LAhD;7EUm5EZn&R!9}_cGeG=V`9Bh6n z>im)Sma&9#a;4jR?|W|l+&z`Vwdxg!O*f;3f|O1Q9AVp}O$4b=L0 zAlMF|9%iB5GYQsiDd$m3IM0^ntNXj__@DBA0E4{$;s)cHe!U zQ!^{l<2y$s4ij{x!XrFoAF=tAAOneo#(L#A<*-WVoi^j!SNUff;AVbhK%c99vU2$* zcg86DBehVB0BD+#pJ2@~>*tFR{|-%6Z8t;WX|G z)ar(<>3#d5G&s`?m>b33Ht6k$<;&o%9xBO@sonsfgYP_od}ZQbBct!w)xSN1$7X_w z*w4y1)`p8(GLzf=37kNj;OAI>-%t~9XZd;n%r{wU`5`SjRZhnXH2^I#Mx>ZWmTrL@ zf6kDHdG|9_Tw6rH&`8HW4=o7E^5zTx13rQ@-x%M~9iy)g25+y^mx3^5o7<4nwm0fO zoizSmofJgtr0r$6S(fMpfr6$3^TeCXf2WDvHf_BF>yDB%VxZ$%JxP% z+|=tQ>hj5~%1R^I*hzj49NDiqiJ#ZoL^m%f6bf%xU zGfw++Jx!6OgxG(ke%@l|6IF8++>k+khUh*KA)@*}{U+0!DbHh)dS{UgOxi?83rAU2bpY8K>Y`lK@ORyn~5T zQd`(PN>SUk9E5B5^>iVW+-jx&wgs)ECb(7l%kAslsss$^z*xrXzF@{Ku>EuB-+V99fXZL0CfN4p76Irf5%rA68kaOyajA#f zhd%BrMz-;74cWD<6!kZeG=N!+>cIq?^4nAaTml(C!_LR}MbM7L1|XvnF_X?{{%q5o z_WDuyUuQ4>@HD;k4g}mFh_2#JD>lf}%>q#!H#jzs#B3yrERNSo%gdXEe{>W{Og=H= zJP+lFZ_6F?duPHf6iXt2 zoCnD;*s)lb$&UhhmR~npZl*TNTgYpf-RAh{yE`2(+DsctW_5tst;0l}Y$IxRH9DIw zi6kcR_y6Pef(e(aZaVR9gFr$mG;2emoHF3G0Dh3^)~gEe(fj_YrhNh=v2UQZvUOn`-iPdl;F9G zK}`|dM0xbCV0C9)Fs)FlAz`$^8*rvqnCZdWgAcUnA06FW`v2?4W<>adJq@27yE1!{ z?$ND6evQP;E=};eeJlO`$l3H2wsJ9J!Z4F` z#sdbwHn!G3QjR^0ii|-=EK7;k6Eq?lnfzD150OU6*7e={bwzR2&Y0El{5rnyWAZ6G zXT$A!vuWOmT_mBaw)Kbs9FpYPEq3CeNm8XqiU02~Nk1k&WqkR+PyfGGV};%aV^HnL z&FDgy?6W5A2~Ec$Rx8YL^w4{D{WAKMc1glci@%$LaY-+-scQVbDTXsh z)LRYZfmh!o3KgfMyc;jGyh#N)*%?p?4hn)lBRoIeTaSvWuBy`El56|%@v6k(d{n-6 z5&kj>Ny-Z4)!ze+( zHnArHE$Y6L19|>T6#D!{3<{-PWGGhNkafJ#AR#E|GErgc4$G)3HfbHdx{3B69L|MH z?EDsz|EoejU$@lk%e^wYDeQEeN2Nud@yWracc&5P!G7s@*%aj@k<;@4p6jnZlc{oU zOjN2+$#lo@zSjIa{9>avUBtX8fCAfgClVs&eRk5D`ifmMFJpheXdp|8ip!98&X=WM z$;>Rv=|I4CJcFwB89RHYrR_pTjJtz8n+h5pB%`bxh5=m-W1(a21;_7BmE@uKSmPc# zh<>IHR~?+OsYR1H-LnW5dvrYEl56_)`rRiQ`C3XF@Bgx$s1QToqwVflt}vV*_&fbN zJKgYJZFayep*<3Z&vuKvSDIOs5{3GnJ&0k~zU?@Bjf8~dF*wX%t~&8BTU%Qib0#ez zeKM+7$>}v>R#rM<*pofC+VhL*k3ik+XX>F`%Hu^1XL&U(C5}!`h@@G&js8)Vb23_? zw;KsCev4kou%8E;bF3@vI@KvDlD@z1s))|UMbG=scw@xzd%aFed zy+A_JAtxgHm|8dfm5(34-Q>aASZUs7D2*Uqq!T|@n*Cf+1;u5A(XYgmj@^H*ad5JY zL#R4B6DBR;UFUIpmXR3V$swp-p397v_PSoSa+34<`2D%Pin-SCaC)(EQ8lI)FZ4$k zPoqspv2NRoQ7Yn=RElQGX9E{V{!WI6NOI~|z*}yGahtSsk=gVl3iCkr69(k3lxyZ` z6eq-_w&bX$6JlwUCmT1xomfqq=g|2k>E)+W<6MYgUm>jbkA~5U=~X+zvKe@Icw#yA zvw}U_R@kKEw2DW(7URs}m-|(OWgkyOFuxuyGTPWbhmerGebUc;aVST%ky>!{xmJPe z`Zzxna@hY=z5+D*m<)Ux^ienXG#0*+w))e&GnNx; z#9@83vx0>P@6UKQ90k0lcax_2)M%dUWw!V6L|yxrz5g{*63K@dP|~A6jpRvEALj}y zr~47Jyw<}UQ(Uo{IyyP4kso&Pnx_|@RgDnrQt5+_R`(jxKTJgN7RU^zjfK8xj)oQ2qO?{m1_t6+c>> z&TH}EWJ%(@4-qH=c)gv{%t(uE#&}LI#O`jkGOS`FeO+>GCfXvHGfO~S zdCj`sroQ|=`IXzm(0zqIBigP^#R1dV(;Tx7Dg@(+-Ky$tbh9J@SgVemHtdcL&NFOVv|bS z3FqxaCFrA32Je$%uVd$S#K$YQB9#flg$X+$AzLnMqm0>cm_3)dmQVnJ5E?;C}QDhPA{?23)hVF?0Tj%FuOpNPEU@iSDo+ zP>BZY!}0!x3%2ZaIDN^+77kkDoLjOLD!E0&=#QMPb!#{~$~QDS2(E!d#^0qR`P(tt znL>`z7u>P7eJn;Xfb7Rc-M(Qd?9gyTM%5gL*4#iu6c}@fxNT*SaV55g(M3usR8Sm( zdOA5dC5@$>4(4epmJFlCJh~9D=#KB{t`8EeB|-G^cVDK8z-T- z$IH%YG8$Zex~Gp@UR~}@ZTxhTN}+-(m{y$nu$rxxSF=!0lgT1PwT9Hb6uG_FjZG_P zmqN`J`B`l{DLLkKB}*6;X~n0drTJXC>Rw+u^dvhcv46(@~fq};~VCHojBh6p$Bn^Dl0k&6%3q1(cOK^SfNI9E& zgM3fsUcl>X#$y8yQx}R>uyP&Ba)1?~?lLKzD6Z8FoldLX6d#OYv`33MHO)%e({`4; ze&i)wODIh@RrA-+w`j~N{_;A4H_QekgMs;iO17)!+i%vn$fm^)9xItBxPC0_RhbcL zUzZiogPJffT~0jPBUzD9E35vDK(Zf*bi*v=B>e3hq^YY5e(>3pbaV(+cSnWoMAnXB z)#>rR#Gmh%7TJzg`qEqN8&0!E_L*ozE&ExVbQnI)uY-n|#3B?cDOs5h)EaA*#4&^H z{=aAa8JxAo$}FQ!q|a#PwPkz!K!@TNYvn|j0jV3#C^jHZ*XX!rt_`Ni+{{Mq;)mQf zlh1rH!5NEwq$H1(GPFs@!Q?%2E-xUK3@hD!O_F~ai?D{?i;0WNeGaRw2W;^W1?yt$ z0qwr~``uzsl6B>uaqFTLn2$WEV+9K&T>L10mEf zmQ}ys;18%w9A5bmM*(!+5-)y`fdx`2xyFuiPqgWM^g#Oqb3pD0|WyI?4F|RKW{W zVaxM@Og$U{eF=C>tJqj(sA>s5fxeqApSkp#wyv5n!(Z%lh!b9rpyGg730YQ*4Krw&RF-P z91j4&ICFHworx?Z8pH&;5YQjCI%lK)SlPD1W;^gM8yxB=U^i)j^h;y}t?42GW27;e zhKyD@%#E6ifkt>(c%}`=!nCT^`y6?^Cqcd0SfANj&0OGp}w`8 z<&`xtUE@-O*=C%}GwY7OMJv?nb|!TNg%6}+KMH9A!Y3)@USOCwE>W2IQZQ(?giH6F z26uNyjKTpavUPZADu|w+Z11W0H#Pei89B|jQ4pEEg~onaQ%38)d(OeZm)sCJY%D5gMkrSjP zMZ4m{xC{j1R;pQMu59Qd$NIX{y$MHg(!?v0E92$)1MxN=a=l-pc;|~-=}T8(zXf>TzsF@c~jYw0IQshj% z7nVt3?MLC?ExJ{LfcuB>ALV!^@fKZJTkqx7z zHo48sZQOLNd^o|rJ~#OHNR@-h(Ap(((UjmwUbijNU5ekGTn&SW&wzIXYZ`fRqI3I# zW-edL(khRZvCHZVgnU_mSfJn&c+JCfjR4;?)Upg0>T5S3y-KImf!w%yn^s3h=VAJ@ zf!Rj?Bm^%H&qd|)I@<{Wb55MK9bPue&z^Ov+#2N;*l&-KkfF0~h}#(yst3r%{#@{9 zK>nq@dGk8HOWvlmu!iko%=?kdWg@dqWvG$@kEUgy^7$ZFuRL#w0d0glgh8!z(dqMv ziL_9v1vjr(4n`OUGaudO< z1ns#c52XsMJwH3nD{$Y%XDTzWJ(CeTE`xPGfSP_;r{*;a@o2fGQeq7ZTkurM)2B~A zXgSbdy7{wute^L8h4n~}Wd-0tG>ZM>UtFy6*T`K*W)?b8-O@*Z!tg;)ooKnT_c!NS z5uZ@t>xHeW&F#L$w43R>$df*wz$o@zzfK~Fl~2FMVl>}VY0vXS1znI3=13U+Fj!<{ zL?z=^f3?vbW8O^1A_8Ad3I08Q__!NPLIl*21)39{qn;fG9HJ}AEGAK+LjVj?sQH(G zH0?ie=pSC2-v+DaY^)-&_t~|UZ*T!|1GJ+RXjYRq;j_y_`P!hT4U8mgn&!dCp_!p-u4;PA?&=6arrn{I^nTr_ zzp!nYc@@@uH%~ZI>Su!O6e~pzSq(&njfI57@@Xj3+xPENx7SzOy1Lfr-vCc@g`Bp(2iemi)W(rdKRiz{PNR{$lx%#7g>szwms)t+=?vXfRYV6 zu45V-8^iY8sa1qoWIh=!Pn$CCA)nm?5-DG^U|DoL?@d@Rq@)07-y9wld}y2$W-E@x zhB%pkfV*^s>qkK5o)h9fc5)st3tpe7bt^sl z`|HPVa-vZDnClv!VS8$|0RYEIyBwFSSC6Y9-b9twD*}JRF|e^uXt4gVB`vgyyZ!Ht<}F{l83Y*7?+O)iPbvH9{Zp zCk~03Kb7wjnqC`X5qx1o@$}zlhBEaYZZCD)8|glshv_X^MAn zs6bbpN`{G0&0bPcA|5yZ96=Ar+Y%r*x#|znO5cM)!%J1DQ~Xnx<#5Iznrw_NTbJU` z5X5eAF%U=={0(m)Xa5>O%+x=W9}_~3mB5sJVkZC3A}f*-nK;qj==DBf3IR>)I@Y&A zhR@Y-$sQ{0&{72wvkAizQ+a?l4+1~?5tTJgwQ7l35LoU(M7u+S-cBGJx>ttv@`l>J zqKxLl58H{U7$7>a5K8Xs&GI@Rf3P_xC)>&AZ~+cx=EmCLN|V;xCi!>6$2!2Jber8p z#uLCm<(mjxrQT0VV{(GZ!>~-SxdYIUBA2xs6X+_gMW+#sC0{gz!UCv~#mb^WVp_mD zpPh{CS30hxZrnDGUXsSOe<^NW?n~co)zJdP3JI0&%8jLE@Y;#MDEhkJn2e2zN+*is z9gsp`F=Z{x;=F@y$_ksne&>}4PP@LMib-~vc55B`29mJ^?RF3a!feXRJ@)NP>Y9?o zJcexqW`*eo2LmFf5uYRwtR34~t@lFb$mvAp@9DJLI*&5F4I=ux{UUSF0LZsN?NQ6ODfjrhy|&c5L7#IiNz7kn~`wog+6dotS&29N8@SIufQzDil6*x>`}vAHKR1C52yf8 zb9Zt2odNy36{xHP#!!q5DG!%wtb5Pvi~I85c`^rq zD_Q%Of>0!lEHYinA1SsTfk07r`0I+gw^{ShROvupXRdbmKC%zIeDlF_nw)9ZSMIzw zA#^XaMp&h$&P_bl%SZkZgUFq}*8p^}AlX}0*h-Y{*+%T*$H*sYHtO?NDbuow!$yb# zVB*k}Ebm5_?g&eDl7zS6eHPi~#Zlhl=EvD_19Htl_sJQ=3)pK<&W<+__(2D)9gi9f ze9Pu5|Jw*F%4wT?4yG@M0ZB{zYOL>)NjcPg;n07TUI~=`D!uFXLp*}i&0P5oP+J2^ z`on$cuMtod9w6(W=DPY7HhlUnYfK&z71r%{U@w!rboH#tdfB>o`SK&H62)YL|A&%czI5~7nnw&)H z#l4Q&NU&bVdx72e*y_xCKD~33p;$S*asRo{WuzseA^vYO7|cysUG+IaT^B@Ph)-0! zK5ae+U_KyZh?ly^E?(#-FcIrXil1p{7>HkrvQhXheqASvbmiZ=KA|i?vbni=aUavY zaTZ4kU+$ISCkX0emYQ?^j#40+inzEqt+GuB6k7l&V&jpoXPF0j88D6O?WUoD^)SFD z$dk?1)-bvn&%-UC!UXN7X+3VQJ{;sF2?J&Y6u$vg7(-X>xDZsW@CULE5~jRpll2ka z+xuh)YHI38XE;}OuYs(C^^nhYd@vV2W9;(hmy~&~%?Cq6!|wy% zpK$Bk%4PL;-3u4KUc-XPR7Bx#a8af%1OPME!}}lz1ROLBcDESBC%4yl+W%<({D{YZ z>?LkJ+T9)xPSunv(cPt0L`^UZg+v0DPUK-f>#ksB@ab~l3$(94l+}*D_f6$u>@z7W zB(j7^7-f+UC@esT+JnNRgVYKOQH^|{F~@T4$?#ql*!^vx@sVsck{Yrlz zUh@&JH8R)gA&w5Xw`}@3!Dny))K;i{IHHZh2Cy!W*tEfGYc|47gdZ4>mYad{l-RCE zx{6oA97$PMTLSK)AW--7(|yfzIdvpeJAR`-x;pu-Gd7o-3Een45C5fYIXnX8Rn5;I-2HY{qRW7S08Yc1K z*WV(ZQUdz7{e<1TwkYJp^q+E%Zp44({0BT2o&OCU1wH8$e(T|{GgTF-ul(^?zcxXv z-FOL6_pheSEP$G+h#zKPw%_-h-shs`Fs%9@Re6w26e&f&Oo%zp!aGs>N_8_F+Lr3- zhFYp@npW2=N=eO6uX!sxpaMv>8g=y#L`ZVVGJ!D8$-Y(z2~kz>RW~$5n|^MOVj~C1 zuxt#Sp6*hl>m+IIX%Apwf1QS+-HO_k;fGiS3{an`v zIBt)!UFeMd1x5WcnRX%`P{`H!^=WBEq1}`_M2SvgNnIEu7SJUV0PY`LBVVmXFzbh4 zTr74cs7Rca8FqJODJAT5{5aX0um>B7zU~;7bcUMI(sa?99pDzRJ?E_7Zc|g?5ggY2 z8S+?6##D_#yh3J!XyE2xbplnm~5R~<#w4F8sT#rlht!B{uv4L$uhwY+Vx=cK7ia- z&l-bXDTz@2gQOoI)BOiYeBc6?L0E&&*SNw2+pTH{`8kv%$|JsA`+hB4;_R*Z%fNs1 zqjw2&p0QFq#P((J3SgO=t0sD*V`4P%ueg4m zC~YRL;-U`o#)!_N8RB?Np?eQ>JlwDh*26|WvVkN6Ewy_*7l3uPGUPb|7ttk?)sXdX z;e5NF>7tB+a;pYNQ+~~?N`ZyZ_d}*4y>dKpuhEN$9QI8ddEyGC_&!!@4mc$u zDT&Ucmm_q4eL|Zl%5ZE3#QIu=dPBt~uW@EDz6N0X>y#5&$adDo2E6vQLHPjnSZZC_ z=m%>H;YySCh)`8ay<+1}P4pg|)Z(#6LNzNu{OBuZxV0N^n5e!CHmT1L?qvQf?~fhm z#@4{DP>*Ym>8jn+oB8n^O%)&kFB`d8!VlqoAflaW5>00`Y>{?lwTL znvDU~rdQ)^t2+n)_AvPj1tv3$=!h5Cp-f-CNPPt*Y&rpwa2Y25!t>zUlxxV6yDi8b zBNeFQ+j3;Mf(L**52t%k0d^nMRLThgN>}uWD({4MZ+j;*QmP${F386<;@WUL5$IIF z=EkBf^jww-C2?X-jlh4zLV(?}1WJ(TYd~BL@TOQdZ5+w+EVQ*IR=(uRJndK;P_DS? z2ynL)O1ZC~q~s{SKw}}1qK{K87Q5mA2&0&pTCrr|zO)NqwtywMmT@(iTf@ng;z~;u zPeMY7`ntGo)rZuOU0A|`2H97gH-O_Kr?@yc5?{Dr*6QKDi9?PRtJr?6y#a zJNDDH!qYczJN7P+wAbUnZbCqR?-XoY(F~koTy;s3mC*_noN%(+Woe|O-wvUk6 z32eH{ro5btJ&A#uD>f4o6E~2UkP?4Akt_Ru^Xmz<9tybzOY)~|>RE64%-O)_&%y#E zzk5^ofQK*B5}EH1zw;!9xYYwM`2BN?$I}1%^#8dUXH>l(6H@5O!*Ylfm6uVL#=Lyz F`(GsjdNBY1 diff --git a/.maestro/enrichedInput/screenshots/ios/inline_styles_removal.png b/.maestro/enrichedInput/screenshots/ios/inline_styles_removal.png index a7b19f9c846b1315de01ae0d6c7970ab6cba5f3b..76b90d4be740f90a9bdc1d399d983b8b976bfeda 100644 GIT binary patch delta 8191 zcmbVxc|4T;_qU`-w@S9knl%YUQ8FQWLS;(~DNFV;#@MffB>R%xkR@A2*$rbtvaez6 zvW_L|V65Z$=)UjY^T+dhey{KI_-9;mUGq8TbI$p^-|us-I)i?LaQ-7HUj+tELUdmh ze2>q`IaMXx8=<12`Xa;|p3dk~#pnYr=B56x7o}HKdBT{4v}W~t3V#HjPhZH*RSsgc zc?pG0me~zF(%Uhbt$6f4D2PNNJ%9G>lXl7yZZqLU|CjN$MClE)%*=pOoZXo*CEn1! zJKO*^Dkr)#Xj?Fsw)Q6FQM|m~K%Rj(To>KJGQYF2khO46<2G_Wqq%x}h8<1={EGgn zK{Hrr5oBXtpQaq-4F52UMQWV3H`K|}>P2Q|;wd|M7*bIu0y&KHAh(F)d$PWM_N6^N z-`(Ap@p@87?l>7Bz0yevr&<$YjgW`=X$&}AE{_VR!m=qU!A(({$J z>+iIJmPwX9A?(u5$>MVR>pBDsT$J!DT{SE;B;*4*Gst+VN7AZY*UEYrD*xJ{A`64T z^{~ja$_qYK@8K`w=8w&8tqVA93VChl=`CM0_+pCp*7-^qi6q9>XYcf&RYlNVWAdly!05yCBJLa34iS)i=T>gNQy~<$(J5qsA4SPH@ySbQmnl!yKUbpk>7dZT7uO@%Em#>9# z!REy_Z#}$|Rh*rzMlkFvv}kdLyWtNgmC&6Zh5DZ#e_Uw3jao8(?O}MNYOPu1#zxP1 zu=n2`AWPEH2FT1v0h{?(&|Gb=HbTm#r;*6^11;;;ovSBO^oU(fv5Fu`3wE4Lcr*|4tGqbWh4qCGV?k5nBpnqlQg4(M4Y}W~;*7%=f zbAIS1QN5D}uY1cNZc>%KP}L}s1cL4M^y%4n{)*n7vFpH_9(s_8+TX^BH*9AR+mn}r zf`iv(DP0-rQM0oz;14aqkI~Du>P3_dg6YD0p5gLWla^EPgk_q#&<}2z^g6iho zw7Uk69u-=3pv~&uZgc2E*zM?OX0Q7K>gUc$J0y_}2v3S%eDxqZ6iI9ekCZu%Av$10 zOtc7lYB$J)$kzt1=&A90`*(XXHDW15h@z@29u>Sd>ai>!AaHCunhp3lf3{MICn7mn zG#TUEir?Q%95t{9$9k#8aRi%5{(u^%=>_tsh{`@2z5Q6fmZa%9F|CIjjBBmMb_jX`@eiH^gL{NS*^-KcQl z1g>(_o={<=1OHH=Z$v3DU4g;@t?j3Opv(V<6!r4-=kTAv{cOc|@}Vj;YbrE=2C|20 zIS%=eiYNM={NX2uAKf7@eN*_4E;GbbQzIiI0ZRyJqxV3z!nj}{3INQY@uRW3Z{=Df zQAfaXTEDD)Gp%CUw*eZ~*Vkil7?F838o(Vn#$Q0S0@7OmIBrm2g7e&Vx&wajIv49a z7rEXm4PxZATWZd4U4djXx!pTK$4`*Q_?dH6T-|XxTshm?0ApO?Mw$WmZiD2=^F%;oNI4I{C(iY0=5fs}IU#KZ(Jxxyd~?tK%?xE0HabAjE|wdL*H z-zA@rJ2cLqo-VeCUHY|{%c_)`rJcgc%sgv-9Nc*-)Ll$?Z{_!!%cp$%kYRdQOD0^x z^Z7afF>GWU^6D39>q(@QBP6sLA`gnL4{-u~nJN`-X+kEKn*F46Ce3KVf~fXdnp<{m zj!%jWRTd8p+H@`{(6)1RvVdnl3W(yC))s3*8E4y*Z@W{dwon%j?jH10`Yc@w5G_o<;a{bYyfO-HUEYzSRO1~H?ofADVdi|wz|*u(wZ zw=20{3g_(vcqtCYPQ|BZ2WM+_C07lpLXc4hCk(nt^1arS-C}pnM?drP{JD{?vA#=b z5yaj*L3I4jw0;x^*%0q2@4wsmd0LZj82fR){Oap_GDrOa!K(R2WwK1g*WGeZjX5uE z&vjjufOW@N^%zP8%lk0AQ~jz-~c?$m>=$*FDq%7;1L2(!a9C)imR64%jdIw3Oe z4v*l21+_ZID0T&al!wUUVWLGuJFpJ4{&p~OwB5Iuh&6OGoEBPvlAB16xmko@^bi>QxQvNc=^SduQ>xb zSZXBQAxfpjM9!EJeZo&dG;5wG#b(Td2MFylvUz*X#|nSao(9C|x5L6Bh>)3`K{H_kk;{GH7WN z`Yh)QDkL~#o8ZPX1wan4+(dk+o^W1k_2$sz7{x00a%4=Dl%RL8RZ#Q0ZWhWkym2V0 z)XjBx#bK!O#z6OXuT5g9EWyGf_Tno=sigh~`(yC7#d9x*R@`R*C5e(9Pb z&YK?JhIjXKmQ@?Vyv=q`E}2&4a6dHtWIK!gD0!;mW(>0R^XF(k#EGcW_m^-*C{Q}G z1$Z(UN92n4;NA7hYW>QnMtPRVCh)EFl6AzLn{;Lsv)dQ9{{j%S8%~$111Y@lzeNpD zsY8;DUEA*Z?x1+6x7q!kqC$SRz-_9?j{LS#sh}v<1wLQI(q4u3DesB7`91r#GO1Xy z(KMv5-FlycL%QGN+N%3p z3e&H9_ZI9lx@})sqGaETFy?!3SL>esy#@Px9PL-*v6a=bJcKjxo|mweD4&Ca6Mf3^ z-BN>UD6tJ_L@SneV6DT#>~%iUBqTs2H^ss0q|CAkT;mE_o-M&F*LNW~Vvj$9oLfeet_ zk7mTOAN-zs+>4e9eIZL+Ny-ksgbIyBF+zggdd-)Y9k1c=2VO8^w_Ns6wIEk7!ot!) z%u4~Q0oP~g#>q)1Z=JAW>PvYn2TqNQGO2Nf<;Sgfzf%u6dwe2~;_vT08dX+tc)NSI z(dZnsU8hDinG43>+iCuc(xQo+i+r)@7!?7t%88&`4;I4?6;Te0btn=P>z`VMS2)!t zg~GF3dWVcV>`>^TwIpX*ivIDu1Y`ptKI;Wr?;%czT_!EAI<`3;0Zhn$UWLugFs`^$ z*1s5qU?T1qQELs}*|_q(Hk}1J(AgOvS>0AaT@Q;)Ld*_dgcEsT3(-e)TAvL_eX{RC z9`9wq@9gI-9h@scMdP_ph(SxT#Buaux-U2A-UYeiKAU`S{?R?VAj5VdyhHU)!Mv!D za_TpL;HAVXH_rN?yOcXlH(3Fk?T=BSN~XUn(#km7ovC4?QgzTpfSmS!u9bUD^8Vzr zk%0jjuq-AR0xf-3FN5rkFAQQ+ZmQ-llp(?sQ#M^#T$; zZlS(*mc;DkvYE4T?F6#1WeopWVrF==mI* znjO@9JJK!VmD2MKDEFPb!woKZ^eT8K31O;l?NI_hZJwH#p~FNPy=j9p>&NX zzTM~{3>k~Z5kFW?Miqs2bG5XH zeXx6VT(N^LgzrKYF=K(=)Fo-q3@x*=d`eR@&W2SrLwT0o^ih#cTfm#XjN?ymJDnAZ zGmx-KLs%b+ccX~(=_Hqz37AWhBMc4b&|c;0Y?cdvoRG@#0C3_zBz8;9(VI>c1Yn!~ zd#`S*@JL&~8?v)azaWuvs4DPUl)azU#8>haxwYgQc^J@)iP2oyFy2gNYQenQbw8v+~ zGXt-)m9XQXDopl?8Mb4I%2Gcok4oUPgo-|*VjqsIfV1iB&ka<ZT8YCVGDmex%FS3^yV3CMC(qe%|z6z2aWY_zkPBC8p7QOesS(; z%`k%45nq24byx*Y!i~b>xX?#E7H2a9#1KGUxV@)0_?esV(c|rL(lgIT3$@AZuzV3? zUFp60E^*)u5J@vWBBA-6EMjME+Y0Y#M%$DJ{yA8{k$-^OPU~ObcHZAX!UPmh|T`qF3K&pF?wwQP3y&fIO1M2e-5xK;(c>zkIjk6s0tyZzlJ8V zj^aR_*B|pfxeLu z^8J#ozoQrL}qm^Sq6h+Rg9RA=mxykm>5` zW@KgLx-ZU7&*fcEzTQmQ*j-->nCS?+6XY3*xY+P}<&CLM-lHGi!&Z~h;JUYQw^I!R ztlUEL%PLHOl=BgWl|BC;Q6!=v$7l@DHqo;h=)Q1{~KdZN-r7fo9H{~7g|N} ze9X$_og{}zCw__k%nTpzmMyHvvIK36J4*8^VUP2yw9+!>L<+~=cLaUlwE?#QY%RWP zH67MaI@jJeIt}-vKu{bxKaK)`F}(hG(^Aa^fJ@soxHT;XmmyY ztHNP%+!aleT&q0Zy2-bReC9J2^YpPx*G6DByigmxm9}>qW<$|B>%(0-z^skgf?v@I zU~%-ehsUYEz7)Qs!z=+oR21JN~+s?;1Y^_G^01ZifeI}yq=-yFJI_&s!!-jc# z3}nTs5tbL!92GbZ;NI<>GRi?IPOOLRoe$vx!FoaGBYdu3Y!PH@ljD!#y)Np2Zp zh71#bN-d;GSl_aqzgsR1iq}5y-{P%|cxyW7olH|(!^)^O3`U?c7Bl1Vma%+BiT3ua zf}*X>E!{eCDG-n^nN1o)s$~WNtunG2u`#g;$u<~u0i&RNNEGPAf(7Goei^<&Pj@4! zKxu5dhLgS%Xg0OF%r%(idZusWHG_s7C{|1Tdw+%3>BVc$to1EvCOoxP(TiU%i41LJ z*C|xzSSx$xXY>L)z9qTJzMla|@Lw27I9R9|KKfgj{g6w8X11r6z;O>MB!} zUsfS&iv0{8w71BDqE3LiofM`QwDpU&cByho@11eeCB4L{6vi}?KXy+4!ZDVSMI>vBqx6vQ_bT2 zs$CJQzJaTxF9V39Q>3#YPZ8BLnfD6fTPXz%A(ucZ;Gi~S@0)>)Tca^wHBV0on2)>! z_*$X#_?Kqw5Jso6XSrkWB6Zu%>vg-m`er9NG*dPV@49r2++F$8PF>8eUZT`BfH@8` zGxJm>RJ~IugLf=qh(-uc&6H%Xy%CIcQGUpE6WR{W9@XQb(&DIruNFAe<|bi6Ts5$D5s z%qce`D?s0|-ad-r4^yw|QQcwOaoBn>(-n5gI|KdE$?+^7kpn$xD3nYG>9)0v8Qz8d zZ8y#M7jf6OvCF4LJd}A>w^k=LV#VA(-CwKwhzQ{A=4BNS-QgMcE!(W>l`A~}Vv1YP z^U}WIaQp#I=mB4QvRs+y^C7_P5}(Zj1)4OfVU&F5_cc~;>49Fs33#=KtJtD4 zCL`6h^b(-=fr4`w0>BUSr7 zZ|J5rr<#}?Xb&w*@rPiG7qGr%c~6*phABG>1#jL^!=}LQqw{csVpu<)(x2slIafkWD)% zpv+YYsY5R4{d;Fqlf0fKI`hcCcEDSif2P(yLfF9@xhUiByoY;`znX!l_S`Nk?(Bxt z4nP}!?AWohuHFaxs3uu|fB(bD_W`D4UW;Ze3k<29=5-FOfbsHq{{HClSuU(r-tONs zeA)kK`u;zS>Y@L~N&f$|*8k5^=Ktv{g#XMiMXuZl%3>Bjb~rPpQdibe!rXoO=6?aE C03BEW delta 7917 zcmaKRX&{vC`?jP|Pem#rlr@C1W?#ydEg@NkWXrA?jO~sVAxnhEHe?Bdv2SB5N|qQ# zc4H~VZtR1xyw~(Rzu*7E|9#&N^WnbdzVGY2&gD3d^SFjgT1;Yij)j-<4xfgUhx&~c zPEt|5P+$yx@%*ha6%`enjgjtpC>;$I)&1)KxoQ44cg*)NCVnp=CsUi&cfD}q{!F#g zk~K}=*UYP2@&ylsH!TFdefze%yL;=_t-f4+lBK_{gv-z>p(|yt1c$p5dUpOnr&&`_ z1G<2Nn0_g6xpL6U0787x&)3O7E%g^9iU?w7Pr(oNw))^XIcc)+2X+$y8<8T^z@&~L zUTW9jGBwGvC%u_!81y|iJSHc!sxaR$cL9ULSwRQJgS)14wX!SY4dj_btA)Nt17BX! z!W|vwKk24;rP;YoG{OdrTN?aHoAfuFn_tmmx*7IA9Iip%IWg$WFWLu%_oeMRF2GF8 z_M)NvJMmpf;u_4yz{++@=Yzoxa#>9onV7N`Q9P28#^+p33rva!;6ZNcU8ypLgU03t z1_s}w*!#l*#v4};KVB>KoYjMwoKf8PQQ_FI(iT|H})hv%}&^=(P9vy>`}x!Oj)#kHN?$EcyYYJAGp^Zi!1`})lL+d z2kx!)oL!LZym#*&4j#0>HE2~^TdR+h@0F{+z|7pViPK)VJ3Ks$L05R;F>sVlg&Tfv z1z(4$Km{CnIgVDjMudlZIVMf!Zl)jY=K?1b7T$oFjOV5Yp`uHn!u|aKir076em5N~ zyQ9h+`#ynp`f@C%pA=a($nx=hdeCHa8u-Sn>k?yLNlA%igC8ma#1rHZ>f@bI)E71> zm$~vL5<+~THIfa}otPf9m+w4?PHYZ+S)mx2RFZbd4GIsPJ~+{>PKg1oQaV8G)N}GwJ-Yf zb;QO3!5K4h3QM%g%ai$8tP)SJI%*8o-Y~1syjT~>F86TUOiL>abp(=-XSaTTy%Tva z-Kz9B6_s+DZ3NwixHuaL^pV5rteaSiKR}&eR&PtYYUBKTVPY2dznU>o# z@##FO_gza6Hb2X$n3|AKr8;JjF}pT`-~$rqUYAyI2GWv$Vi#fuhJHXDsB zTdi#FbFxa8L@e^6&RABv;aA3LvovmOELTq~9N&K%5z$9W`+atB`2%!Bo>=)cnQJ8R zv`5)E>hdjB=6Dh?8+GT#5o7N%w70c5o-g2A?tmTO29=%>Ri;K09vRuM;MgVXNANTt zfOss(D#cTRir(NB7Z=|dmHY(!vKJ8E`g^`FH;}yDAG-T|XVp5;!vmE6Xw5Sl1qlmK8y5#pgVxpeNW@}wI+ve$ z28t#s_fMTZ{T+DdoQS^IyH~V3aoKDVSe|jwKuveS#qOx|5-e! zdOwb%r?_k_FRu#p^>XW$lUSdrY}2E?#bUS`gWFim8s2B(%$3_cWM?>s*iN=q9FN%i z%L5?+fnng{66=KgII=^^Oga?j{#F3!7C8HTYMXvh06Y8LcMP<Zc zI^xgVcLb4$ANs9$Z`~s&=-L7Vv~_nU z=$Xh8CW6Si#DXeN*rZ9288~uBoySy&5x@izh_;^iSG)etv!(u!+UNYcoSB>?YRtwtbl3tOHMIuF7_AF}80#nag zXHi-k6&041k&em$0Lx2;V9NDvI&l5}zSIQ$@38mY)v<>~W;sX6lw3V~jr!QkpO+V^ zZfabme1wiSZuk5Rh=GfNaz&fp$(Wa&ot>9A*B-|Y9c&F|fxJ`nH&D~pkC!|>UPN(p=(f*hRC}{3yKL&Bk@8eZsYX`;&_7@ zd#IBYji7+Q`)SYR5y?Om$^);$C8lu++MZ}Ke6&E9F|Vd(2P_%Tat8cYLhsm}qgD(% zceX#7&g<2$%K?K-raZyo9c;~XeK5G=oBN+THxlUT0&#c~a*)41w(2yReS zsuk6++b+_1kyc;|`II|Lx+A{$;nA=_?jF#fwk#;^J?~6*(%?aZ>yQLBz8+zS<)S(& zTQ5`8+o|c?7AdN#Vy*D+2`;DgIsstxsg`zeU4Vay4GqR~HVUakbqH%~>QSm{svKT- ziR5DCP1WszD9!XEZ)C&Dmg{G4#yNoAegrC-3-2#k6F5my@@_R~fXH+*jzsu(PPgrF z-Dbi06Mu@ddq8tQjG2BrUQB%WC)x zP?osHzuX|Piy@v9@>k0MEoWY)6RUqa@g}77?C@(KQ|Q+-2-jKX2A}POqrYpQUm;&l zgm$yz`aNC*I*^hY4y6Sq=E7slQ!9q#UhRyXT;j+$hpvhj5NgohZ0(lzVh6jutr=HL zJ}fCSpn0JE*kRTVt0-0nd@|mUR=f3fzI1i2d4st-<$Iv45UvYyqYSKiC!9&X<0gHwFJqgfz&rm36C`bi?4*W!RMmPT0Da)JH1 z;25jA#5g7 za#4d^`zecC9i3K>5$5V~Z#p0#6Z>j&Q`Dry>qZAg%^)ag$L^u!!(IA)D~IRUAF}cV zj<$y7je%a|)cPy1aLnsJ?3PVyv^Y-r_A4do_zxfUcYa5&Xzl)*W43o56+yxL^!#=a zeP`3<#sZvcM^!qbg!|O;%>NMN%J&rqxcx)+LjlyUy-gEb2%ZwQ8fP%=&$=)4AT%B&_43?h=iI8Ej*{&=caX`XMhRmbIYWlGdTX zn{wEf9Orp$)_40Ey(9Cw=v<(r#z`e8$zO~nTCF_{=ZTxqooDY)NJ~shNh~<}`I_#6 zC3YpVPGJ#y6;uGX;~|`buJ)!z2ERVi_mhorhhTZ}!S+t|Q4%UVdnP+FjPv(!rdX!e zYY4%-E|u%b!698gy6vW0CGjl%pjH?QA0D^U9E&-_FJ*M(V~|?y3uIH^)s<5VSzIt(E*yDw<`Oq^%MILy6k5s$KC7Z(!Zow8`2J|5>! zT*5m!3t<0Dma^(*_^UfX1<9JCVmGTj#= z3z=^eb}FI9`KRDb^&OI7!C?oojGtot1h@x*F)gpc>GNM{YTQ zd>M+oOOG`>XE>GcIc1On}q3SY5UKycU4V`nu zkNd96S)=>_{j!W8+k>zzC*HNsqcwrQ%Qniku3H9px4fT!uYz1$R&b|1lB~~rcb#a3 zSg$=Zrf^eSG>*<_>fuYqITqOFeoVm0V^lBBygrbA&~hahP>QLf(~eDJRwd8e&v3EQ z)N)x|)Fk#Re5&(_?!TF74K`}$3BA{Gg(kL0lT9&kvGF*_&)k`)a$`_BV1Iex3cmJ% zRk&g2+mw{l=GnTK<#?i1{>>8zpB511n228r$xW`-I8}u_(O#C`V=Gn&VzRdfD`Q@B z(I>7Ram?KAwcWo!P58&sWf-bFeJw+Mu-T_i@Y)<6{_f% zqW|8xh}X~b_&(k-qJ?Kne->qG z`rz~aeaciL!}=xl!<)DU6WQOzv5jJjX-vVk7#=h7sI4v5)QN{$AuD2KyHJUet5yHa zRTH$utz4VIeAt%Mp!jIoY?VAedvsI$7nD7I4NedkQL6+vkrmMq;J~5VXBN9M#L-mBllAMb<}iuvQZ*JM;NM z`J<{xM>cqx3cRH_=mUzUNiBkyK7c=c}FS!V%UtBRLVIX9%|lqU_wYd$uakqB*8S{zl# zyG_)zZMLzttBSVP&#~01+&vDJez7qe;oJDuCe(XzCauW>K(_w|gX4~u0U|ae+5G_9 zaWrl%D%o%5?ws?a^VerG_}Rk3xXk>9}NDzCF%mHm|O-0 zkfJUnxyXBcA#**-mc#K%@1q7mt{C%3jjN=G<;?hHQ;3rCX<9Tr`By8NuLN%fOwXow zD=cr&A0zRxmv17h_<~p6M-N}7o;Ym+8=oJ%X!CUY5o;=DnJAGp}qma=~3 zfl_8;69jpYVyqRmK%`C+Y6ToUtVY&icSm^q<}<~{@g)^S!4tlXi`O^SCEq>0JgFVH zs9z}komkeOeUs5=`~C33YsTO)q#6xsEdt3IOTp<98Gr?y-i+5L;GUCBq@!x5Z}rvP zy=e{{NYi27pNcv=J>sgr-?jnGe|wkw3C6afR_>nin*U%#O5Z>6gG4Or^JZH-4x4w| z-nIE`HyTdqM|2D*RY5nE3?^hS<+BVdro?Tm&e>p2wVRy7iQ^_jG<`bf?pOd(my+)RtR`oW(*;A-if{?B927uDtj`oNsrT!`m52%Y)j4O=$p@fS6pj z>4d+Ic;}9uJFwjM$22Q#rCtA%&l#*ub8gr@h+>M19>tVDmTWB;EAU>FiAC5x!~K%> zZv2>l^>#nQ&3f|DP14w|r~TSzJS7V*O%HH?3Z4CIDd#u;u`{(p!48?(++TsRpF@89 zQhz!9C&nY!Q-ARkbN?CW+2(0rjh`L{F&S5pP+l#5U|%qNB#50R&3{SONT(Ib~04_hIO?d@JzmdKt2-8D6{ zOt{jldB=7|3pVw@5MR>7MlDEvLaDK>t?OnqFLmuOKlN_i|6b-Ibm79BKz{bmrJvWH z-7)g7Ak+|Neya8a<r+Yd(5?Y+Xejt57RQ`jIB@id^;Z;vh*fQSrRR!p5d@Y0XHfB|AHHI)QF4~ zlu3X?KP+yNa@a-ZPcd&@X)b@7*7qFu!|;ZVSssWj;*3l zE9`$S7M~z|DltAWJ~e)6m`8IiAJw*V@Vkre2CtBsNsUCb%SwJy4U{1mX^8(B26)i0I=;SM5mZ|4 ztyrl8qZI)K>qGV~c_;73ls~Vk`TV1hG*Ia z?9EeNYn?7%FWbC8m`!i;2y$GBOqPZc!??S$a6}hM3=-sC0Q58|{`~Q24`V`HZGEMG9N(j417p6x zqHOMs)(4gzd)ct4HTmRUiG=TEV7;uc1yFWt`%S2e?*;YnAspt$a6 zwbX#-JkZ8=(Z!90x-fF{Hgi4ORmeO}SQn7;zX*rXTFH(?hs8iQ;FjnKCFsy=#Zlp| zTBdkrS33QvlC7~`Pk=Ll0SwbHtgL)zz5e;f{(*ywD&8^IR^Hg{pPEo5&uu@;{k;x5 z_aX_>qd_eZh&Qe^{@V~=l*I8_kq)fwis_6jECK)rAn6>>#KoCAa`1~M?bdsh&aO_Fw~Hw;t>K_@2S?rEoaYi` zXnT@}E=eiM)(s394do>EcAN8}TknHjHL@utBQ~>Ma^^eajir2TlL?*m9nj*ZHul`` zN$9cqceYf%tmQAN+qgn*>9Azfln0_G5SEbwanQcU+}Ovr6>rtFA`~@uCW{o@x1yJo zWP>(C={fH~mwX5ik^bjf&snT%=TH@BsyeLx|Hy<@5z0B}(2zgWk|KjDFJBXGDB?J_4x~syVRESv zxJOm_K{tr5KSh^2rUp7vB!z%mFU&MXd>sjJ$enFFQaQ7vq$fb+kI>wubzIc7er0L- z>i}ck&q`US864hh)r0;|Rl@0rcda=LenO(-0q1VuSfexX7v=k-vEHy`Z$~^_+Fhyg zDs`;_V*S-9MaO+{Mk`BX8M89Ej2Zx>wCQlKAR|LrUxxCRM-0Kj%nM&RYAzh1?53Zt z2!v>>VvE|$V#+69I1s_n*M~P2(8zOq{@}?$v;VJ>}3*#pC0^Ql0uz9C#8fm1rz zG*nRura@B9U&QpSt*wE~z0%}dgkcKa7XX5E>fQya%V6H^%T+r`FU2c@2}SoU1W~jw oDynl7Nj;91iR1tCJI5SzbPoI6{|G)}_GedGYI=9ks*gkd4-ebe$p8QV From bd9ec44b7a0d6419d65607d0e089a6a13598b3ca Mon Sep 17 00:00:00 2001 From: Krystian Sienkiewicz Date: Wed, 5 Aug 2026 15:40:46 +0200 Subject: [PATCH 4/5] refactor: neutral character set optimalization --- ios/utils/ItalicUtils.mm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ios/utils/ItalicUtils.mm b/ios/utils/ItalicUtils.mm index 5b6ec0bb2..c518674f0 100644 --- a/ios/utils/ItalicUtils.mm +++ b/ios/utils/ItalicUtils.mm @@ -15,8 +15,8 @@ typedef NS_ENUM(NSInteger, ItalicKind) { ItalicKindOblique, }; -static NSCharacterSet *NeutralCharacters(void) { - static NSCharacterSet *neutral = nil; +static NSCharacterSet *NonNeutralCharacters(void) { + static NSCharacterSet *nonNeutral = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSMutableCharacterSet *set = @@ -25,9 +25,9 @@ typedef NS_ENUM(NSInteger, ItalicKind) { // ZWS [set addCharactersInString:[NSString stringWithFormat:@"%C", (unichar)0x200B]]; - neutral = [set copy]; + nonNeutral = [[set invertedSet] copy]; }); - return neutral; + return nonNeutral; } // returns YES when the font renders the given UTF-16 sequence itself @@ -123,8 +123,8 @@ + (ItalicKind)kindForCluster:(NSString *)cluster font:(UIFont *)font italicFont:(UIFont *)italicFont hasItalicFace:(BOOL)hasItalicFace { - if ([cluster rangeOfCharacterFromSet:[NeutralCharacters() invertedSet]] - .location == NSNotFound) { + if ([cluster rangeOfCharacterFromSet:NonNeutralCharacters()].location == + NSNotFound) { return ItalicKindNone; } From 43e32ea3dfdc4eb9563c7185a226364e9e85e617 Mon Sep 17 00:00:00 2001 From: Krystian Sienkiewicz Date: Thu, 6 Aug 2026 21:06:19 +0200 Subject: [PATCH 5/5] refactor: stylePriority rename --- ios/inputAttributesManager/InputAttributesManager.mm | 6 +++--- ios/interfaces/StyleBase.h | 2 +- ios/interfaces/StyleBase.mm | 2 +- ios/styles/ItalicStyle.mm | 4 ++-- ios/textHtmlParser/TextHtmlParser.mm | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ios/inputAttributesManager/InputAttributesManager.mm b/ios/inputAttributesManager/InputAttributesManager.mm index 5d5abb213..17e011754 100644 --- a/ios/inputAttributesManager/InputAttributesManager.mm +++ b/ios/inputAttributesManager/InputAttributesManager.mm @@ -62,11 +62,11 @@ - (void)clearRemovedTypingAttributes { } // Paragraph styles always go first, inline ones are ordered by their -// stylingPriority +// stylePriority - (NSInteger)stylingOrderFor:(StyleBase *)style { if (style == nullptr) return NSIntegerMax; - return [style isParagraph] ? NSIntegerMin : [style stylingPriority]; + return [style isParagraph] ? NSIntegerMin : [style stylePriority]; } - (void)handleDirtyRangesStyling { @@ -106,7 +106,7 @@ - (void)handleDirtyRangesStyling { // Sort style types so paragraph styles come first. Their broad visual // attributes (e.g. foreground color, font) are laid down before inline // styles override them on their specific sub-ranges. Inline styles among - // themselves follow their stylingPriority. + // themselves follow their stylePriority. NSArray *sortedStyleTypes = [presentStyles.allKeys sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(NSNumber *a, NSNumber *b) { diff --git a/ios/interfaces/StyleBase.h b/ios/interfaces/StyleBase.h index b59e75308..343f88de3 100644 --- a/ios/interfaces/StyleBase.h +++ b/ios/interfaces/StyleBase.h @@ -14,7 +14,7 @@ - (BOOL)isParagraph; - (BOOL)needsZWS; - (BOOL)appliesStylingToTyping; -- (NSInteger)stylingPriority; +- (NSInteger)stylePriority; - (instancetype)initWithHost:(id)host; - (NSRange)actualUsedRange:(NSRange)range; - (void)toggle:(NSRange)range; diff --git a/ios/interfaces/StyleBase.mm b/ios/interfaces/StyleBase.mm index c4ebeed5b..c6e14b088 100644 --- a/ios/interfaces/StyleBase.mm +++ b/ios/interfaces/StyleBase.mm @@ -46,7 +46,7 @@ - (BOOL)appliesStylingToTyping { } // determines the order in which the styles are applied -- (NSInteger)stylingPriority { +- (NSInteger)stylePriority { return 0; } diff --git a/ios/styles/ItalicStyle.mm b/ios/styles/ItalicStyle.mm index 873bb6039..39b965eb4 100644 --- a/ios/styles/ItalicStyle.mm +++ b/ios/styles/ItalicStyle.mm @@ -24,8 +24,8 @@ - (void)applyStyling:(NSRange)range { // some styles might apply a new font (inline code), so we need to apply // the italic last, that way knowing if the used font supports italics // or we need to apply a slant -- (NSInteger)stylingPriority { - return 1; +- (NSInteger)stylePriority { + return 3; } @end diff --git a/ios/textHtmlParser/TextHtmlParser.mm b/ios/textHtmlParser/TextHtmlParser.mm index 8f388b943..df5a8e472 100644 --- a/ios/textHtmlParser/TextHtmlParser.mm +++ b/ios/textHtmlParser/TextHtmlParser.mm @@ -157,8 +157,8 @@ - (void)applyProcessedStyles:(NSArray *_Nonnull)processedStyles { NSArray *sortedInlineApply = [pendingInlineApply sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(NSArray *a, NSArray *b) { - NSInteger aPriority = [((StyleBase *)a[0]) stylingPriority]; - NSInteger bPriority = [((StyleBase *)b[0]) stylingPriority]; + NSInteger aPriority = [((StyleBase *)a[0]) stylePriority]; + NSInteger bPriority = [((StyleBase *)b[0]) stylePriority]; if (aPriority == bPriority) return NSOrderedSame; return aPriority < bPriority ? NSOrderedAscending