?????????????? PK!r=_ f fdashicons.woffnu[wOFFf FFTMDrL"GDEF` 3OS/2@`icmapA@gaspglyf Ylhead\.6 hhea],$hmtx]H@r loca^Pvmaxp`` Yname`V/*vpostb F?webffV=c-xc`d``b `b`d`dd,`9xc`f?8Ue4ʹ)Ja~ _bDR6 xݐKA璨7 Ł$4$"Hhb!] ;[E;F j6y$`aoyÃ` ɀ;Xq9e!A&A6 K#S@SL5ڠh_YVU^jVվ늩S3u0-s`86((@i&1}rUN[Km*SAntCS6K]i9d~Ns( ?_oqK88Ξtvmg]媬ɪ\rN#ĵLNV?TX6ytxڬ |U8\;[[t,7u=l! K/FMnQAPE7D\F"aFmD&CqduFs I_N:̛t[Un'rGNݜIKW]o{*-dcX3LhCg<3G*_ziH(LLf+1[`HCdP@L9%Y0 Ca>x0ְ;geM+ݺ6HieŒ7ji?Ref6Ow[ʳ$-tK^plfeKʕͺU#X!^.*5j. ^m2 R)!:G;iu(MB*׶ƚ[4rzm+ݺ^[ u|"}}1ѐO$ЭIM{66,$;|[FU,o`=zwTKN47Wȵ\6^Sdi$A]%MÎ%H)(%l$abf7oeDjiill|q"}RzUda -&dOнW)MMHXrWk[?7*[c߰Mr;y3Yny45r Gib#Dw]vJ&:U R/u, :;th+~z~#uu©Xn;zp%<}N.+5 X|Twr #T^Jd?0'QO:&;$h. 8p'?K85gq֋!"g] =n#iPX@_"͹=Yo_WONU[Zv-nnbbGn':wJYincw-=;'(`'gt 3 \ivδ-& gCI0/^2{!ޅײvEBy*WΔDDQvVu0 5} !OhR }oUd⏷#}Fi/5dLWWgMPhnڠ5Xp ;=N6?Bk6-^,R&3gWTb9\ +ݪj?4 Ayř3zX= NY3(rY!Oʌ0tH1Wr~C۷:37_x|G^AOsŖCmmWK#U@uqz 5,vo bcB yEߔA8`;LiOQ݈ % Gc `H\:Fڃ\B+ֺ7=HVri;li `0AOpΆOsCD;IlL5<ugԑӑhhie\ϾK>vpTIi;} " b6w&E]r/&սG?[MȊvʲäQ4w8&9Ϋ`y( h70<ɑ~\òo\UUʛ2?{ {)FKymkۊ'9ִ}H_"~82.!>ڣmb% /1\0dJ+^vߞW(`Q wA(\F$Nq1)9`lg_&^d';VRzxgo g}$}/Cwq#(Kl3Hi~8HH{i B9/76?rHg g9'::c=d)'tf)CX AMeHV<5(E]uG@soq’-pɗ"v[OE^>oj4D$<^$z'.pe8Y^=ߐ3zgW :k$@Z7 "\g''^F_Fmۉl8H1!@e#lqe .CM&8i4Q0`Bn׷9Z8E͡g_w݋#4W+~KOQ/J<~=xvg`\zj#\@ۑ=zȼ!p|laYNl$t AF̓[?o aKbC&籙ux$އ& o - &^mǩjKQGNBFOĚ?EP(MڦL!5’"hf^/mh(=]2bZiCwC4C}ƫ-`an!DHϲ{ +&ˑ&ו:JZhQړ\ri5HKr X%֫KA$PfFxD Hɳgt%2@\w%51*XK\J<m eG;O_E"?18v~eYyŌrtݍf`&NYLŚ9#1PCE~m]INӐlI4Oos3+ޭ4M8tPyx$0VH,<{4évcmX+C |1CHFa?w$R(i*ӋL *ǽˮAh'Q w݈gsT-QdNȟFH/cSc4R  c5T0*h?@ 0 sQrr:Dcr t/ Ǎ?ƨK'oB!y:㫿7 Hs-n?/gJ^MxV7nu`~>PHEtg#\}w:1?L>LLJh77ӵl\"@d3 :Qn30](Huu"d A\G|ֆFI;.B3=QЈ=:o:=-;vtc+`o{k煢!)x:nEQ`C$CE 1/" ܮq"8`C80A|ڟ}{]J-*x䏯]6T4Q|Y'(qelP9Aqp(&U+i~ͿJE:[UsK 8)rQ?]7VaRؙ3)?sFmNy8_%v?7>^GG[Snz}6RE'w|FGc&khGp Ty NxB0=L 51d;M@ҞMpy,5߽:;ҀAC$ b Dꖎp^n,Y-&~2t,OrzܙS%3=n,eM6ՑKuaEX`r8&0Uٽ|-;p7_FHcѢE/"Rt}IʳUH5U 6a[/0!bby0H;c!&)=j:~$Ԭ؉5ǀp(`(ITTK:T-Kp> MK7sSu!nǡͼ`t.H63#@=θ5589ISc|̸|!sEMVňċ(C$7\g@r]V/ŃF.AqL?H;S;ҥ0nݗDcO4ҥDRyG\1LU.ѯH(2wx\j\ͩ)8/mt8d_ Kzu},Z6l]Y?Ȃg6Ohhw_Ĥɱ㧌f"#]*x.-'C>7>z±Ha!$&zq8PwF5HG: >oV4(", 0y<;iKs˙J6"1څ#bx=q͈Y⡖٥бp^9gd5n=Fo`3CۨkӲx oXQQﰘt9Ƽl1ۜ.i qR(Oo0z+ |f$ϚW<47(|db+%w4hU_k6a4sPfҫ"E'9EY\^="-bjrx^W-`')} f`ĠҀH(k(=l|ꦛtX[>Mp W?pĀ=J55whl롏'沉A۹ܿhIp'{q?& UkK~ I:Os8\![)ƌ+REփ:r^(BrFd'drss(۸9( r+&LXJ'8_ȧ qȁ-:`@Eb01Ttf1p[gX[yaYEeUKN,;c,6̚?:?XpӔ -:ɠYUkȖM6[Ax @+jRwZ7ri}S4`S {[4Mr:g /l3}ٱŲEC(n"^72Pֻd]1yD= 2s1 bHǷm޸ M/m"DU]$6I&ڼ>ߑ|߀Aw(:ud>A1.Dр!<-̱f9t$` L*_8C |Z({JB\J ȑ.a2ޛm`0ڛm1L<"?//oPB:ȗa%\˨z|mee Ef[Q][Y?f]٨88Cv ̾8m%>E(Vm̀IL:}ze%1Q!6B{Fidj|}?.=.~Pn9) J'GYɬ%&>Uggyr֑_^^Ro+dYN;ydbJyYo#FΞ-vN;41{jzZjwddb02. =$h%,z4gWg/U] W3ߒio{>TH,Z;$j55Dde>n?{Pve:ifsiMߎ (.@M;./?aG=N)C~9ۛ7aNVgtP;SoF# i oLUH5qD:Y:5}\A d*zaGoBE-Avy({EoWf7cQt'"ӄ-':@kStG;tմݨx׉|by'v2~w@teSMj2X4|M"tL ,"+|MvCu?\G}!9? !*)T7C~TCŕyr}Rd.@%owrv`rYJOΛʲ 1\WMG\phmO&Wĥ]%EÚqm\}dKc{)[kF/%匃x!lQT 3yE`dh" :eD"B'7͹5?F3"q` 3w.|tU]6TֶN[+I25kc30L/j454-!9ʴeh#fV KBBӡ N)G)w(!+;*d'3U .$e5&fgY 3: bV\!]+?8lu(/E x*vƀWonVD?GOv+xl~ .k眺\k[{;ECھChkXg09bV \ $Gsf6+xDAcL0/\ǵa !Gk4:N.̂Bv dH!ɽnyDy W3>I$I<"}ŽyVl؋9bvk,%Ufї/AXhRu#TŞUNc-)TPts oa;J^H^ $_V ̿F]o Ge9&PF? ݬ2Ynެ Qݼ*w=ikL ^^<t*بiFkhSxzLp7OIT?vTRyeD'/"PL0Sr\&)̀4,' 06 T$9(/:M{"躗kEUL1&r)uK-'im'G;oTN}SU*;e;?"st*R# Yji 9HOx͌V>23xrTl|3~|KbݟB8/qH.~slI!û<@}rD_;AtGIeڶiSצG.r!q{=vͷM?kaz}ۮ9c3Lgn^nfY T/]\tтG_=b-~Aw \N?ǻyg~w;]>f:;Y|oOogş \d]띗tO[Bz}AM9Yy)U^l,DNZl=4(!G4[5f[Q_ij_hSNǗׇRk-,%t>q钔1;fUW\uֹI˝92/퓭ŕ0.z☑I#Sc7e~bݷHkIzlqsZ ,LTtdP1`!T*`И ʋhR:ڹ ,G2;!\Qh@$`fBHY%7~"aRJ",L9rdɭK/d֓[D]©~w=*)ˢ[hwk(9pQg4ɉ$Mumb7̯ G& ~&EQC>b 9 ɍoю_o" #O=u."\}4Mm=u\V'; o>mM\u"q/̭d}q &$n`^գj2B,qy>6{df4V %rNyDNKQE u7AȐӻw&hYjblڌnw9윬Nh{w+2jH{wȠ&ܽ?ݭ~O S986+vH "5VŮ58Ɉ dG=Hg/Ҁ3_>\頏Ӊ.T EuKCRPQJ DUO7)` fz& Dt_?2  ٢!ܠaqQ({9 r dU#gUɼ,=aXUؑv۱#?nyl\G$A a۬&oj~K$Ceio fO>?*C#Nf{9Y|(?9_&;&;1)Օ :(92g"L.ernTPe.PЋ൐Fe܄;7 `bU-w̽~Iz*6!a.} ՚lJϷNn$*Ixi wJ kR0W'M['+Flk^]=<㶧rmѭNQnJسhIBA2"FɤbwZwV:Ħ?F|WgllO{׻;.*nM;,g<ͭ (`Y >9&?CQK C!gf+gK0zqQg zu-5Q#0#D]9d~^N dKhl~[y &P)5E{Cq# uFjA덮UWGWVTY I'ש| Cz!tvP@Sie>>;:y?~ m!ɚ}a҇˕Oas?7֕DbߙEht/rsA %3I:3t*]a2YͣLt,KN$''۔!k;-ruUU ^v o{ic^ *E`\q>VYu/Oq۝0й&hw;]|{ 9:iiij:7EzvK#Ӟ+G ֎p3w{Uf'DELpgI]јPG:oto*RNJ;WG,Vb%:V2{g{93P_ T'95bNDV#QvU 3 Rl3H>뙍^/G6] PˡCP&!5FQrd@;*UBECpCT)t2fQm?ܑT)cr]EReᎤJ*I|'܍(8._w<.}TA>_G,B>Ć{`'AϠ\D S!$.GH#6'CK,AHfRQ z9"#@_E?Lk\?ǪE[wص\8ݳ7b~6*Wg==ͫNOɯm{~CG\1ԕΩjcF6>UEຯ[kV=֯ T||,rE}Q1|ryyz) X A~(0}xI"k #2zqIiL[+fİO8}UU0@[WG\3hK, UY*HGb"` %d) @7Y.E]Yf7lA#.G![vWQ0űDIsHQ=v84OvTFvP@]Y#c{h_L|㝮cK,/N\#6t_ (%sj>l +B?OWzʟvO=E'{)2ڳ˟_I&椄9558|ЋùQ8n"7 .J>Pl.:瘄 61 ˝>V]_ mag6Ũ ȣG*ól9).\?as~Fd[x#$1+o%E>U3p\U2r`ӛ"_)sMcGuWWk>ӛ}ke=fڛ{nwf^xXmMM ճs)O4KO}5cqԄ}TƂI X" ,Y@ҒסoDD+ J 'ښޚA,&ۤ '}˭o-zVoukA+4hq%_hb5M'dE,+uS޶ϝc%W5O8-Hw x~γٗ cЛɚ?e*NЃE:~^kϏZECO'v]KK )U)ΉecX]BԤja!ڕdAZ݅?y,!Q0ό ;쮤0]|~^zfϬ==k7:ztהz$ڭzgC?$y7Q߳gϬݻ/}G$ cVNUYfȪfr ,CjTBH٫GT.{1|oRځbuA4=o}ym&;GҟFƋ`TOz|2I!.\IE Dz%t%_<_|9Wd蛱/1?_"vT.bUpaVB~ a"0:B+1`{#f!(5=gu5vlɵ@CktѲxk*c+c 5$(h ހ WnN+edKA }H'@ƐKe)颶(ķxЉw^xON21W5FoV@&Wd;ETMTRsv2"*hk@9Ȫ$fY7[,iR5T~-*?͙c.\8N'/w;+yWZk}SnFlֽo?WCdW[I WtB9`Hu޸ZWFoQexV9 &p|\jR7W2-kj;^˹MQBD`cux+/N17x; `g)Mc&Ѫ}w"(Ð8@dE "IMѾCx-ڃZxLbBr(9bf;N6f)3p D9rQNh@k  oh8u,:,oh_xS`Jw+YY]^% '0} KA S`/P]­6p[λzY `dEvfD+ z 2#h}2)2k4#CrV:/h%:vq,*}bR$& )M?f~Ʒ֭{~KO}kݕ%W7ZP?8鶀ak?˵\lg >t])'S%|'"%7!XS:]nzYW]YWC5V`;Yz/[16i/5 [*BSߡIZjwu\DI"Cxk>!*|L1*ϴ5"]73Eߊ+һocb2Nj֝;3DJR=@`}GٕHk8Wg<-`-gȿsL;Sqz< .洤\-3/~EozvKvcfZ.l.){\rg&s J(P?K'^uPkt֗+-ٮkz9a2w+Zմtzz~H].iˡ-LnnrOH 0 0Vr &3~hHl ΝZ"+d`H_N{?z5vhV0 0:,"h#YRd?RCė!y`2хv[L^k]0i1 :n^~8 {7z}*Lh5ƫ2569ETI@9cCЏ;reǪ *9!W Dz$g_{i>yb)coO%Ya+HjMKO=~'浮e@W\JH}]x$c'MڝOI>#߱-x3t r#o [HJUt i=J gi7Dդ\4⍤Q,+}hBT@EHH*ܵzBUB6ߑ-fUhZROa-mT!WI-Uq!6Y/=VQ D&N8Yx~ί?ȂwN/XHB2&-MMUЯK fy>p#́?f=GbZ_Զ'GYcI1Lʇ#X)Ld 24)3K7ctw: &BA &>6eW|mk2Xas6YfۘSkM)7ט%}n^eOZj'秩$)Em lÖD/^aG}?vsjڜS{ dԡ4!vF$'Qv%ٗzfHIㆮK fm-@byyWHXp4" Ff06?fGV=HC/JЏv E9Fq$NzX͌a$ɮDd@" 'lMiAD8h0h! =)%{Jܚi?[i/xJ7Ig0VbL)-!lv% ` VV~hCgp f,α4yM&WG#ՑHbzne2[e xDm!_~>zhˑc%9'1{G5qTx9+{>DY82#ߏ9(Z`P틣~4 rd~ˍE@b#R%6X"^O+:fyfg}}OX9NlizB=CqM9d!LFİnTą Dx@T<с_ɋmoxNwh3J2to,Жh3XDDx$5!.ԗ{xkB^S:i5@u^#5]xlᙆH|wQ[j4$MҤiӴMC[ZZ>ã/Be节պ 2*C[(uuUAgwvqGv(q97>ڛs?" Bt2_JNL@2&< j"h&#(f,$п4MpIah 5p@"\&9+IEehNWˑŤprm!yŒanTB&: iԻUR\BYM ͶrtBŌU3gEX67-I,ѩ%{0RPDW ]sEx^df.M `dZy6\ÛSe`gO8<'Q䵗R CN@%gGL ,/ M4I +i!F d7fj VSgi-iF%݈LΞ;m״7 魲a]uӼ&ənj0Iz?ɰxvk i3Lt ojYbi'5 sZifZ˨rT@F+>Nr4{ NS.g{Cƣk0|lBwAs 22<|I'\%>olQ1*@3V06M7:FDy9gdh5ٲ‚ȭXzq9攕9P4,;q~겱Wwjլ ڔKW( &#]r~fUMsm-v{Aa&NyCvI0O7ϫ-}1 )D? -AQ RpΌANA_;rFhxN+AoI\0+ \`^ݿ}Q[o([W=.rښk`Ԇ]GK-+6mK2Bc /^,;r3w=Bs64lKݵkUԬDMzH׵;],5::KjMU%fˮ]'&?M/ߕWps:fœO%cfђ&F @ ? J3?=%"yڥyŠzҽ|+8wy^tt;&Jc_ܲԑR[~=8 c*]ׇͿ <3,n鈮~Q670>eX<\ g1vy|OBZV9ь)Ӡgpv&axo6|羺ԁͻ i>0P~'N { jZhʔUNf0&3-#.ơI|z/|d;Zb60^xhvkwבpuS;Z=­j|!1j[8O\_3`4`ȲRJ$B?)5 ?tɾǛq\X|sWTnׂ J:2ZqL>}REl?H-\Xpp =ȿ=ft i4XbN,^_ Y,"9%EXOl"d1a4C]h7u^_=< FG ٗ!76tlڀGF3jzay8O]P̢[.aXȽt4u/'@ >3yr +PY.<gxm _?-ؖYP|10lGya @]{CJ{wx<h5o}_jWHSǢ T/X՜t/\qBm%\m4m,A6'r 6r{a;loĩ~855.Z0bD6Pv   " 0 > L , b , t J 4 d ^|2| Hb>Pz R N2B L&^2:j4j>vr  l !!!"$""#`#$V$$$%%@%%%%&'@'((*(@(Z(p(((())&)*H*++,--L--. ..\..//\/00T0001161X12v223 3344466J677J77828T899R9j99:<:t::;;f;<0b>>>>>??~??@P@AvAB&BdBCCbCCCD2D`DE EHE`EFF@xc`d``dc L@ `>xڍRNA=c,,,F5!*"h.2"0S`㙻!lc&3{{<F>684~5cHiC>c>S>sKk[{~G~g~W~wOoadww bQ91+3,f٘J^%WɫTr*9JN$9IN$9INdgg,'ɞϞ//~")/_<>'|.'|>jrj9ZN-x6r9FN#iis_collection_registered( $new_collection->slug ) ) { $error_message = sprintf( /* translators: %s: Font collection slug. */ __( 'Font collection with slug: "%s" is already registered.' ), $new_collection->slug ); _doing_it_wrong( __METHOD__, $error_message, '6.5.0' ); return new WP_Error( 'font_collection_registration_error', $error_message ); } $this->collections[ $new_collection->slug ] = $new_collection; return $new_collection; } /** * Unregisters a previously registered font collection. * * @since 6.5.0 * * @param string $slug Font collection slug. * @return bool True if the font collection was unregistered successfully and false otherwise. */ public function unregister_font_collection( string $slug ) { if ( ! $this->is_collection_registered( $slug ) ) { _doing_it_wrong( __METHOD__, /* translators: %s: Font collection slug. */ sprintf( __( 'Font collection "%s" not found.' ), $slug ), '6.5.0' ); return false; } unset( $this->collections[ $slug ] ); return true; } /** * Checks if a font collection is registered. * * @since 6.5.0 * * @param string $slug Font collection slug. * @return bool True if the font collection is registered and false otherwise. */ private function is_collection_registered( string $slug ) { return array_key_exists( $slug, $this->collections ); } /** * Gets all the font collections available. * * @since 6.5.0 * * @return array List of font collections. */ public function get_font_collections() { return $this->collections; } /** * Gets a font collection. * * @since 6.5.0 * * @param string $slug Font collection slug. * @return WP_Font_Collection|null Font collection object, or null if the font collection doesn't exist. */ public function get_font_collection( string $slug ) { if ( $this->is_collection_registered( $slug ) ) { return $this->collections[ $slug ]; } return null; } /** * Utility method to retrieve the main instance of the class. * * The instance will be created if it does not exist yet. * * @since 6.5.0 * * @return WP_Font_Library The main instance. */ public static function get_instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } } PK!b~""class-wp-font-collection.phpnu[slug = sanitize_title( $slug ); if ( $this->slug !== $slug ) { _doing_it_wrong( __METHOD__, /* translators: %s: Font collection slug. */ sprintf( __( 'Font collection slug "%s" is not valid. Slugs must use only alphanumeric characters, dashes, and underscores.' ), $slug ), '6.5.0' ); } $required_properties = array( 'name', 'font_families' ); if ( isset( $args['font_families'] ) && is_string( $args['font_families'] ) ) { // JSON data is lazy loaded by ::get_data(). $this->src = $args['font_families']; unset( $args['font_families'] ); $required_properties = array( 'name' ); } $this->data = $this->sanitize_and_validate_data( $args, $required_properties ); } /** * Retrieves the font collection data. * * @since 6.5.0 * * @return array|WP_Error An array containing the font collection data, or a WP_Error on failure. */ public function get_data() { if ( is_wp_error( $this->data ) ) { return $this->data; } // If the collection uses JSON data, load it and cache the data/error. if ( isset( $this->src ) ) { $this->data = $this->load_from_json( $this->src ); } if ( is_wp_error( $this->data ) ) { return $this->data; } // Set defaults for optional properties. $defaults = array( 'description' => '', 'categories' => array(), ); return wp_parse_args( $this->data, $defaults ); } /** * Loads font collection data from a JSON file or URL. * * @since 6.5.0 * * @param string $file_or_url File path or URL to a JSON file containing the font collection data. * @return array|WP_Error An array containing the font collection data on success, * else an instance of WP_Error on failure. */ private function load_from_json( $file_or_url ) { $url = wp_http_validate_url( $file_or_url ); $file = file_exists( $file_or_url ) ? wp_normalize_path( realpath( $file_or_url ) ) : false; if ( ! $url && ! $file ) { // translators: %s: File path or URL to font collection JSON file. $message = __( 'Font collection JSON file is invalid or does not exist.' ); _doing_it_wrong( __METHOD__, $message, '6.5.0' ); return new WP_Error( 'font_collection_json_missing', $message ); } $data = $url ? $this->load_from_url( $url ) : $this->load_from_file( $file ); if ( is_wp_error( $data ) ) { return $data; } $data = array( 'name' => $this->data['name'], 'font_families' => $data['font_families'], ); if ( isset( $this->data['description'] ) ) { $data['description'] = $this->data['description']; } if ( isset( $this->data['categories'] ) ) { $data['categories'] = $this->data['categories']; } return $data; } /** * Loads the font collection data from a JSON file path. * * @since 6.5.0 * * @param string $file File path to a JSON file containing the font collection data. * @return array|WP_Error An array containing the font collection data on success, * else an instance of WP_Error on failure. */ private function load_from_file( $file ) { $data = wp_json_file_decode( $file, array( 'associative' => true ) ); if ( empty( $data ) ) { return new WP_Error( 'font_collection_decode_error', __( 'Error decoding the font collection JSON file contents.' ) ); } return $this->sanitize_and_validate_data( $data, array( 'font_families' ) ); } /** * Loads the font collection data from a JSON file URL. * * @since 6.5.0 * * @param string $url URL to a JSON file containing the font collection data. * @return array|WP_Error An array containing the font collection data on success, * else an instance of WP_Error on failure. */ private function load_from_url( $url ) { // Limit key to 167 characters to avoid failure in the case of a long URL. $transient_key = substr( 'wp_font_collection_url_' . $url, 0, 167 ); $data = get_site_transient( $transient_key ); if ( false === $data ) { $response = wp_safe_remote_get( $url ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return new WP_Error( 'font_collection_request_error', sprintf( // translators: %s: Font collection URL. __( 'Error fetching the font collection data from "%s".' ), $url ) ); } $data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( empty( $data ) ) { return new WP_Error( 'font_collection_decode_error', __( 'Error decoding the font collection data from the HTTP response JSON.' ) ); } // Make sure the data is valid before storing it in a transient. $data = $this->sanitize_and_validate_data( $data, array( 'font_families' ) ); if ( is_wp_error( $data ) ) { return $data; } set_site_transient( $transient_key, $data, DAY_IN_SECONDS ); } return $data; } /** * Sanitizes and validates the font collection data. * * @since 6.5.0 * * @param array $data Font collection data to sanitize and validate. * @param array $required_properties Required properties that must exist in the passed data. * @return array|WP_Error Sanitized data if valid, otherwise a WP_Error instance. */ private function sanitize_and_validate_data( $data, $required_properties = array() ) { $schema = self::get_sanitization_schema(); $data = WP_Font_Utils::sanitize_from_schema( $data, $schema ); foreach ( $required_properties as $property ) { if ( empty( $data[ $property ] ) ) { $message = sprintf( // translators: 1: Font collection slug, 2: Missing property name, e.g. "font_families". __( 'Font collection "%1$s" has missing or empty property: "%2$s".' ), $this->slug, $property ); _doing_it_wrong( __METHOD__, $message, '6.5.0' ); return new WP_Error( 'font_collection_missing_property', $message ); } } return $data; } /** * Retrieves the font collection sanitization schema. * * @since 6.5.0 * * @return array Font collection sanitization schema. */ private static function get_sanitization_schema() { return array( 'name' => 'sanitize_text_field', 'description' => 'sanitize_text_field', 'font_families' => array( array( 'font_family_settings' => array( 'name' => 'sanitize_text_field', 'slug' => static function ( $value ) { return _wp_to_kebab_case( sanitize_title( $value ) ); }, 'fontFamily' => 'WP_Font_Utils::sanitize_font_family', 'preview' => 'sanitize_url', 'fontFace' => array( array( 'fontFamily' => 'sanitize_text_field', 'fontStyle' => 'sanitize_text_field', 'fontWeight' => 'sanitize_text_field', 'src' => static function ( $value ) { return is_array( $value ) ? array_map( 'sanitize_text_field', $value ) : sanitize_text_field( $value ); }, 'preview' => 'sanitize_url', 'fontDisplay' => 'sanitize_text_field', 'fontStretch' => 'sanitize_text_field', 'ascentOverride' => 'sanitize_text_field', 'descentOverride' => 'sanitize_text_field', 'fontVariant' => 'sanitize_text_field', 'fontFeatureSettings' => 'sanitize_text_field', 'fontVariationSettings' => 'sanitize_text_field', 'lineGapOverride' => 'sanitize_text_field', 'sizeAdjust' => 'sanitize_text_field', 'unicodeRange' => 'sanitize_text_field', ), ), ), 'categories' => array( 'sanitize_title' ), ), ), 'categories' => array( array( 'name' => 'sanitize_text_field', 'slug' => 'sanitize_title', ), ), ); } } PK!=class-wp-font-face-resolver.phpnu[ array( 'fontFamilies' => array( 'theme' => $fonts, ), ), ); return static::parse_settings( $settings ); } /** * Parse theme.json settings to extract font definitions with variations grouped by font-family. * * @since 6.4.0 * * @param array $settings Font settings to parse. * @return array Returns an array of fonts, grouped by font-family. */ private static function parse_settings( array $settings ) { $fonts = array(); foreach ( $settings['typography']['fontFamilies'] as $font_families ) { foreach ( $font_families as $definition ) { // Skip if "fontFace" is not defined, meaning there are no variations. if ( empty( $definition['fontFace'] ) ) { continue; } // Skip if "fontFamily" is not defined. if ( empty( $definition['fontFamily'] ) ) { continue; } $font_family_name = static::maybe_parse_name_from_comma_separated_list( $definition['fontFamily'] ); // Skip if no font family is defined. if ( empty( $font_family_name ) ) { continue; } $fonts[] = static::convert_font_face_properties( $definition['fontFace'], $font_family_name ); } } return $fonts; } /** * Parse font-family name from comma-separated lists. * * If the given `fontFamily` is a comma-separated lists (example: "Inter, sans-serif" ), * parse and return the fist font from the list. * * @since 6.4.0 * * @param string $font_family Font family `fontFamily' to parse. * @return string Font-family name. */ private static function maybe_parse_name_from_comma_separated_list( $font_family ) { if ( str_contains( $font_family, ',' ) ) { $font_family = explode( ',', $font_family )[0]; } return trim( $font_family, "\"'" ); } /** * Converts font-face properties from theme.json format. * * @since 6.4.0 * * @param array $font_face_definition The font-face definitions to convert. * @param string $font_family_property The value to store in the font-face font-family property. * @return array Converted font-face properties. */ private static function convert_font_face_properties( array $font_face_definition, $font_family_property ) { $converted_font_faces = array(); foreach ( $font_face_definition as $font_face ) { // Add the font-family property to the font-face. $font_face['font-family'] = $font_family_property; // Converts the "file:./" src placeholder into a theme font file URI. if ( ! empty( $font_face['src'] ) ) { $font_face['src'] = static::to_theme_file_uri( (array) $font_face['src'] ); } // Convert camelCase properties into kebab-case. $font_face = static::to_kebab_case( $font_face ); $converted_font_faces[] = $font_face; } return $converted_font_faces; } /** * Converts each 'file:./' placeholder into a URI to the font file in the theme. * * The 'file:./' is specified in the theme's `theme.json` as a placeholder to be * replaced with the URI to the font file's location in the theme. When a "src" * beings with this placeholder, it is replaced, converting the src into a URI. * * @since 6.4.0 * * @param array $src An array of font file sources to process. * @return array An array of font file src URI(s). */ private static function to_theme_file_uri( array $src ) { $placeholder = 'file:./'; foreach ( $src as $src_key => $src_url ) { // Skip if the src doesn't start with the placeholder, as there's nothing to replace. if ( ! str_starts_with( $src_url, $placeholder ) ) { continue; } $src_file = str_replace( $placeholder, '', $src_url ); $src[ $src_key ] = get_theme_file_uri( $src_file ); } return $src; } /** * Converts all first dimension keys into kebab-case. * * @since 6.4.0 * * @param array $data The array to process. * @return array Data with first dimension keys converted into kebab-case. */ private static function to_kebab_case( array $data ) { foreach ( $data as $key => $value ) { $kebab_case = _wp_to_kebab_case( $key ); $data[ $kebab_case ] = $value; if ( $kebab_case !== $key ) { unset( $data[ $key ] ); } } return $data; } } PK!I##class-wp-font-face.phpnu[ '', 'font-style' => 'normal', 'font-weight' => '400', 'font-display' => 'fallback', ); /** * Valid font-face property names. * * @since 6.4.0 * * @var string[] */ private $valid_font_face_properties = array( 'ascent-override', 'descent-override', 'font-display', 'font-family', 'font-stretch', 'font-style', 'font-weight', 'font-variant', 'font-feature-settings', 'font-variation-settings', 'line-gap-override', 'size-adjust', 'src', 'unicode-range', ); /** * Valid font-display values. * * @since 6.4.0 * * @var string[] */ private $valid_font_display = array( 'auto', 'block', 'fallback', 'swap', 'optional' ); /** * Generates and prints the `@font-face` styles for the given fonts. * * @since 6.4.0 * * @param array[][] $fonts Optional. The font-families and their font variations. * See {@see wp_print_font_faces()} for the supported fields. * Default empty array. */ public function generate_and_print( array $fonts ) { $fonts = $this->validate_fonts( $fonts ); // Bail out if there are no fonts are given to process. if ( empty( $fonts ) ) { return; } $css = $this->get_css( $fonts ); /* * The font-face CSS is contained within and open a