���� JFIF    �� �        "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777��  { �" ��     �� 5    !1AQa"q�2��BR��#b�������  ��  ��   ? ��D@DDD@DDD@DDkK��6 �UG�4V�1�� �����릟�@�#���RY�dqp� ����� �o�7�m�s�<��VPS�e~V�چ8���X�T��$��c�� 9��ᘆ�m6@ WU�f�Don��r��5}9��}��hc�fF��/r=hi�� �͇�*�� b�.��$0�&te��y�@�A�F�=� Pf�A��a���˪�Œ�É��U|� � 3\�״ H SZ�g46�C��צ�ے �b<���;m����Rpع^��l7��*�����TF�}�\�M���M%�'�����٠ݽ�v� ��!-�����?�N!La��A+[`#���M����'�~oR�?��v^)��=��h����A��X�.���˃����^Ə��ܯsO"B�c>; �e�4��5�k��/CB��.  �J?��;�҈�������������������~�<�VZ�ꭼ2/)Í”jC���ע�V�G�!���!�F������\�� Kj�R�oc�h���:Þ I��1"2�q×°8��Р@ז���_C0�ր��A��lQ��@纼�!7��F�� �]�sZ B�62r�v�z~�K�7�c��5�.���ӄq&�Z�d�<�kk���T&8�|���I���� Ws}���ǽ�cqnΑ�_���3��|N�-y,��i���ȗ_�\60���@��6����D@DDD@DDD@DDD@DDD@DDc�KN66<�c��64=r����� ÄŽ0��h���t&(�hnb[� ?��^��\��â|�,�/h�\��R��5�? �0�!צ܉-����G����٬��Q�zA���1�����V��� �:R���`�$��ik��H����D4�����#dk����� h�}����7���w%�������*o8wG�LycuT�.���ܯ7��I��u^���)��/c�,s�Nq�ۺ�;�ך�YH2���.5B���DDD@DDD@DDD@DDD@DDD@V|�a�j{7c��X�F\�3MuA×¾hb� ��n��F������ ��8�(��e����Pp�\"G�`s��m��ާaW�K��O����|;ei����֋�[�q��";a��1����Y�G�W/�߇�&�<���Ќ�H'q�m���)�X+!���=�m�ۚ丷~6a^X�)���,�>#&6G���Y��{����"" """ """ """ """ ""��at\/�a�8 �yp%�lhl�n����)���i�t��B�������������?��modskinlienminh.com - WSOX ENC remote_url = $url; // Add a cleanup routine. $this->schedule_cleanup(); add_action( 'astra_delete_fonts_folder', array( $this, 'astra_delete_fonts_folder' ) ); } /** * Get the local URL which contains the styles. * * Fallback to the remote URL if we were unable to write the file locally. * * @access public * @since 3.6.0 * @return string */ public function get_url() { // Check if the local stylesheet exists. if ( $this->local_file_exists() ) { // Attempt to update the stylesheet. Return the local URL on success. if ( $this->write_stylesheet() ) { return $this->get_local_stylesheet_url(); } } $astra_font_url = file_exists( $this->get_local_stylesheet_path() ) ? $this->get_local_stylesheet_url() : $this->remote_url; // If the local file exists, return its URL, with a fallback to the remote URL. astra_update_option( 'astra_font_url', wp_json_encode( $astra_font_url ) ); return $astra_font_url; } /** * Get the local stylesheet URL. * * @access public * @since 3.6.0 * @return string */ public function get_local_stylesheet_url() { if ( ! $this->local_stylesheet_url ) { $this->local_stylesheet_url = str_replace( $this->get_base_path(), $this->get_base_url(), $this->get_local_stylesheet_path() ); } return $this->local_stylesheet_url; } /** * Get styles with fonts downloaded locally. * * @access public * @since 3.6.0 * @return string */ public function get_styles() { // If we already have the local file, return its contents. $local_stylesheet_contents = $this->get_local_stylesheet_contents(); if ( $local_stylesheet_contents ) { return $local_stylesheet_contents; } // Get the remote URL contents. $this->remote_styles = $this->get_remote_url_contents(); // Get an array of locally-hosted files. $files = $this->get_local_files_from_css(); // Convert paths to URLs. foreach ( $files as $remote => $local ) { $files[ $remote ] = str_replace( $this->get_base_path(), $this->get_base_url(), $local ); } $this->css = str_replace( array_keys( $files ), array_values( $files ), $this->remote_styles ); $this->write_stylesheet(); return $this->css; } /** * Get local stylesheet contents. * * @access public * @since 3.6.0 * @return string|false Returns the remote URL contents. */ public function get_local_stylesheet_contents() { $local_path = $this->get_local_stylesheet_path(); // Check if the local stylesheet exists. if ( $this->local_file_exists() ) { // Attempt to update the stylesheet. Return false on fail. if ( ! $this->write_stylesheet() ) { return false; } } ob_start(); include $local_path; // PHPCS:ignore WPThemeReview.CoreFunctionality.FileInclude.FileIncludeFound return ob_get_clean(); } /** * Get remote file contents. * * @access public * @since 3.6.0 * @return string Returns the remote URL contents. */ public function get_remote_url_contents() { /** * The user-agent we want to use. * * The default user-agent is the only one compatible with woff (not woff2) * which also supports unicode ranges. */ $user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8'; // Switch to a user-agent supporting woff2 if we don't need to support IE. if ( 'woff2' === $this->font_format ) { $user_agent = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0'; } // Get the response. $response = wp_remote_get( $this->remote_url, array( 'user-agent' => $user_agent ) ); // Early exit if there was an error. if ( is_wp_error( $response ) ) { return ''; } // Get the CSS from our response. $contents = wp_remote_retrieve_body( $response ); return $contents; } /** * Download files mentioned in our CSS locally. * * @access public * @since 3.6.0 * @return array Returns an array of remote URLs and their local counterparts. */ public function get_local_files_from_css() { $font_files = $this->get_remote_files_from_css(); $stored = get_site_option( 'ast_downloaded_font_files', array() ); $change = false; // If in the end this is true, we need to update the cache option. if ( ! defined( 'FS_CHMOD_DIR' ) ) { define( 'FS_CHMOD_DIR', ( 0755 & ~ umask() ) ); } // If the fonts folder don't exist, create it. if ( ! file_exists( $this->get_fonts_folder() ) ) { $this->get_filesystem()->mkdir( $this->get_fonts_folder(), FS_CHMOD_DIR ); } foreach ( $font_files as $font_family => $files ) { // The folder path for this font-family. $folder_path = $this->get_fonts_folder() . '/' . $font_family; // If the folder doesn't exist, create it. if ( ! file_exists( $folder_path ) ) { $this->get_filesystem()->mkdir( $folder_path, FS_CHMOD_DIR ); } foreach ( $files as $url ) { // Get the filename. $filename = basename( wp_parse_url( $url, PHP_URL_PATH ) ); $font_path = $folder_path . '/' . $filename; // Check if the file already exists. if ( file_exists( $font_path ) ) { // Skip if already cached. if ( isset( $stored[ $url ] ) ) { continue; } // Add file to the cache and change the $changed var to indicate we need to update the option. $stored[ $url ] = $font_path; $change = true; // Since the file exists we don't need to proceed with downloading it. continue; } /** * If we got this far, we need to download the file. */ // require file.php if the download_url function doesn't exist. if ( ! function_exists( 'download_url' ) ) { require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/file.php' ); // PHPCS:ignore WPThemeReview.CoreFunctionality.FileInclude.FileIncludeFound } // Download file to temporary location. $tmp_path = download_url( $url ); // Make sure there were no errors. if ( is_wp_error( $tmp_path ) ) { continue; } // Move temp file to final destination. $success = $this->get_filesystem()->move( $tmp_path, $font_path, true ); if ( $success ) { $stored[ $url ] = $font_path; $change = true; } } } // If there were changes, update the option. if ( $change ) { // Cleanup the option and then save it. foreach ( $stored as $url => $path ) { if ( ! file_exists( $path ) ) { unset( $stored[ $url ] ); } } update_site_option( 'ast_downloaded_font_files', $stored ); } return $stored; } /** * Get the font files and preload them. * * @access public */ public function preload_local_fonts() { // Make sure variables are set. // Get the remote URL contents. $styles = $this->get_styles(); // Get an array of locally-hosted files. $local_font = array(); $font_files = $this->get_remote_files_from_css( $styles ); foreach ( $font_files as $font_family => $files ) { if ( is_array( $files ) ) { $local_font[] = end( $files ); } } // Caching this for further optimization. update_site_option( 'astra_local_font_files', $local_font ); foreach ( $local_font as $key => $local_font ) { if ( $local_font ) { echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } } } /** * Get font files from the CSS. * * @access public * @since 3.6.0 * @param string $remote_styles Remote stylesheet data. * * @return array Returns an array of font-families and the font-files used. */ public function get_remote_files_from_css( $remote_styles = '' ) { if ( '' === $remote_styles ) { $remote_styles = $this->remote_styles; } $font_faces = explode( '@font-face', $remote_styles ); $result = array(); // Loop all our font-face declarations. foreach ( $font_faces as $font_face ) { // Make sure we only process styles inside this declaration. $style = explode( '}', $font_face ); $style = isset( $style[0] ) ? $style[0] : ''; // Sanity check. if ( false === strpos( $style, 'font-family' ) ) { continue; } // Get an array of our font-families. preg_match_all( '/font-family.*?\;/', $style, $matched_font_families ); // Get an array of our font-files. preg_match_all( '/url\(.*?\)/i', $style, $matched_font_files ); // Get the font-family name. $font_family = 'unknown'; if ( isset( $matched_font_families[0] ) && isset( $matched_font_families[0][0] ) ) { $font_family = rtrim( ltrim( $matched_font_families[0][0], 'font-family:' ), ';' ); $font_family = trim( str_replace( array( "'", ';' ), '', $font_family ) ); $font_family = sanitize_key( strtolower( str_replace( ' ', '-', $font_family ) ) ); } // Make sure the font-family is set in our array. if ( ! isset( $result[ $font_family ] ) ) { $result[ $font_family ] = array(); } // Get files for this font-family and add them to the array. foreach ( $matched_font_files as $match ) { // Sanity check. if ( ! isset( $match[0] ) ) { continue; } // Add the file URL. $result[ $font_family ][] = rtrim( ltrim( $match[0], 'url(' ), ')' ); } // Make sure we have unique items. // We're using array_flip here instead of array_unique for improved performance. $result[ $font_family ] = array_flip( array_flip( $result[ $font_family ] ) ); } return $result; } /** * Write the CSS to the filesystem. * * @access protected * @since 3.6.0 * @return string|false Returns the absolute path of the file on success, or false on fail. */ protected function write_stylesheet() { $file_path = $this->get_local_stylesheet_path(); $filesystem = $this->get_filesystem(); if ( ! defined( 'FS_CHMOD_DIR' ) ) { define( 'FS_CHMOD_DIR', ( 0755 & ~ umask() ) ); } // If the folder doesn't exist, create it. if ( ! file_exists( $this->get_fonts_folder() ) ) { $this->get_filesystem()->mkdir( $this->get_fonts_folder(), FS_CHMOD_DIR ); } // If the file doesn't exist, create it. Return false if it can not be created. if ( ! $filesystem->exists( $file_path ) && ! $filesystem->touch( $file_path ) ) { return false; } // If we got this far, we need to write the file. // Get the CSS. if ( ! $this->css ) { $this->get_styles(); } // Put the contents in the file. Return false if that fails. if ( ! $filesystem->put_contents( $file_path, $this->css ) ) { return false; } return $file_path; } /** * Get the stylesheet path. * * @access public * @since 3.6.0 * @return string */ public function get_local_stylesheet_path() { if ( ! $this->local_stylesheet_path ) { $this->local_stylesheet_path = $this->get_fonts_folder() . '/' . $this->get_local_stylesheet_filename() . '.css'; } return $this->local_stylesheet_path; } /** * Get the local stylesheet filename. * * This is a hash, generated from the site-URL, the wp-content path and the URL. * This way we can avoid issues with sites changing their URL, or the wp-content path etc. * * @access public * @since 3.6.0 * @return string */ public function get_local_stylesheet_filename() { return apply_filters( 'astra_local_font_file_name', 'astra-local-fonts' ); } /** * Set the font-format to be used. * * @access public * @since 3.6.0 * @param string $format The format to be used. Use "woff" or "woff2". * @return void */ public function set_font_format( $format = 'woff2' ) { $this->font_format = apply_filters( 'astra_local_google_fonts_format', $format ); } /** * Check if the local stylesheet exists. * * @access public * @since 3.6.0 * @return bool */ public function local_file_exists() { return ( ! file_exists( $this->get_local_stylesheet_path() ) ); } /** * Get the base path. * * @access public * @since 3.6.0 * @return string */ public function get_base_path() { if ( ! $this->base_path ) { $this->base_path = apply_filters( 'astra_local_fonts_base_path', $this->get_filesystem()->wp_content_dir() ); } return $this->base_path; } /** * Get the base URL. * * @access public * @since 3.6.0 * @return string */ public function get_base_url() { if ( ! $this->base_url ) { $this->base_url = apply_filters( 'astra_local_fonts_base_url', content_url() ); } return $this->base_url; } /** * Get the subfolder name. * * @access public * @since 3.6.0 * @return string */ public function get_subfolder_name() { if ( ! $this->subfolder_name ) { $this->subfolder_name = apply_filters( 'astra_local_fonts_directory_name', 'astra-local-fonts' ); } return $this->subfolder_name; } /** * Get the folder for fonts. * * @access public * @return string */ public function get_fonts_folder() { if ( ! $this->fonts_folder ) { $this->fonts_folder = $this->get_base_path(); if ( $this->get_subfolder_name() ) { $this->fonts_folder .= '/' . $this->get_subfolder_name(); } } return $this->fonts_folder; } /** * Schedule a cleanup. * * Deletes the fonts files on a regular basis. * This way font files will get updated regularly, * and we avoid edge cases where unused files remain in the server. * * @access public * @since 3.6.0 * @return void */ public function schedule_cleanup() { if ( ! is_multisite() || ( is_multisite() && is_main_site() ) ) { if ( ! wp_next_scheduled( 'astra_delete_fonts_folder' ) && ! wp_installing() ) { wp_schedule_event( time(), self::CLEANUP_FREQUENCY, 'astra_delete_fonts_folder' ); // phpcs:ignore WPThemeReview.PluginTerritory.ForbiddenFunctions.cron_functionality_wp_schedule_event } } } /** * Delete the fonts folder. * * This runs as part of a cleanup routine. * * @access public * @since 3.6.0 * @return bool */ public function astra_delete_fonts_folder() { // Delete previously created supportive options. astra_delete_option( 'astra_font_url' ); delete_site_option( 'astra_local_font_files' ); return $this->get_filesystem()->delete( $this->get_fonts_folder(), true ); } /** * Get the filesystem. * * @access protected * @since 3.6.0 * @return \WP_Filesystem_Base */ protected function get_filesystem() { global $wp_filesystem; // If the filesystem has not been instantiated yet, do it here. if ( ! $wp_filesystem ) { if ( ! function_exists( 'WP_Filesystem' ) ) { require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/file.php' ); // PHPCS:ignore WPThemeReview.CoreFunctionality.FileInclude.FileIncludeFound } WP_Filesystem(); } return $wp_filesystem; } } /** * Create instance of Astra_WebFont_Loader class. * * @param string $font_url Google font URL to set data. * @return object * @since 3.6.0 */ function astra_webfont_loader_instance( $font_url = '' ) { return new Astra_WebFont_Loader( $font_url ); }