    def add_font_face(self, rule_descriptors, url_fetcher):
        """Add a font face to the Fontconfig configuration."""

        # Define path where to save font, depending on the rule descriptors.
        config_key = str(rule_descriptors)
        config_digest = md5(config_key.encode(), usedforsecurity=False).hexdigest()
        if self._folder is None:
            self._folder = Path(mkdtemp(prefix='weasyprint-'))
        font_path = self._folder / config_digest
        if font_path.exists():
            # Font already exists, we have nothing more to do.
            return

        # Try values in "src" descriptor until one works.
        string = ffi.new('FcChar8 **')
        for font_type, url in rule_descriptors['src']:
            # Abort if font URL is broken.
            if url is None or font_type == 'internal':
                continue

            # Try to find a font installed on the system that matches descriptors.
            if font_type == 'local':
                # Create a pattern that matches font name.
                font_name = url.encode()
                pattern = ffi.gc(
                    fontconfig.FcPatternCreate(), fontconfig.FcPatternDestroy)
                fontconfig.FcConfigSubstitute(
                    self._config, pattern, fontconfig.FcMatchFont)
                fontconfig.FcDefaultSubstitute(pattern)
                fontconfig.FcPatternAddString(pattern, b'fullname', font_name)
                fontconfig.FcPatternAddString(pattern, b'postscriptname', font_name)
                result = ffi.new('FcResult *')
                matching_pattern = fontconfig.FcFontMatch(self._config, pattern, result)
                if matching_pattern == ffi.NULL:
                    # No font has been found, abort.
                    LOGGER.debug('Failed to get matching local font for %r', url)
                    continue

                # Check that the font name in descriptor matches name in font.
                for tag in b'fullname', b'postscriptname':
                    fontconfig.FcPatternGetString(matching_pattern, tag, 0, string)
                    name = ffi.string(string[0])
                    if font_name.lower() == name.lower():
                        fontconfig.FcPatternGetString(
                            matching_pattern, b'file', 0, string)
                        path = ffi.string(string[0]).decode(FILESYSTEM_ENCODING)
                        url = Path(path).as_uri()
                        break
                else:
                    # Names don’t match, abort.
                    LOGGER.debug('Failed to load local font %r', font_name.decode())
                    continue

            # Get font content.
            try:
                with fetch(url_fetcher, url) as result:
                    string = 'string' in result
                    font = result['string'] if string else result['file_obj'].read()
            except Exception as exception:
                LOGGER.debug('Failed to load font at %r (%s)', url, exception)
                continue

            # Store font content.
            try:
                # Decode woff and woff2 fonts.
                if font[:3] == b'wOF':
                    out = BytesIO()
                    woff_version_byte = font[3:4]
                    if woff_version_byte == b'F':  # woff font
                        ttfont = TTFont(BytesIO(font))
                        ttfont.flavor = ttfont.flavorData = None
                        ttfont.save(out)
                    elif woff_version_byte == b'2':  # woff2 font
                        woff2.decompress(BytesIO(font), out)
                    font = out.getvalue()
            except Exception as exc:
                LOGGER.debug('Failed to handle woff font at %r (%s)', url, exc)
                continue
            font_path.write_bytes(font)

            # Create Fontconfig XML config file.
            mode = 'assign_replace'
            root = Element('fontconfig')
            match = SubElement(root, 'match', target='scan')
            test = SubElement(match, 'test', name='file', compare='eq')
            SubElement(test, 'string').text = str(font_path)
            edit = SubElement(match, 'edit', name='family', mode=mode)
            SubElement(edit, 'string').text = rule_descriptors['font_family']
            if 'font_style' in rule_descriptors:
                edit = SubElement(match, 'edit', name='slant', mode=mode)
                text = FONTCONFIG_STYLE[rule_descriptors['font_style']]
                SubElement(edit, 'const').text = text
            if 'font_weight' in rule_descriptors:
                edit = SubElement(match, 'edit', name='weight', mode=mode)
                integer = FONTCONFIG_WEIGHT[rule_descriptors['font_weight']]
                SubElement(edit, 'int').text = str(integer)
            if 'font_stretch' in rule_descriptors:
                edit = SubElement(match, 'edit', name='width', mode=mode)
                text = FONTCONFIG_STRETCH[rule_descriptors['font_stretch']]
                SubElement(edit, 'const').text = text
            match = SubElement(root, 'match', target='font')
            test = SubElement(match, 'test', name='file', compare='eq')
            SubElement(test, 'string').text = str(font_path)
            edit = SubElement(match, 'edit', name='fontfeatures', mode=mode)
            descriptors = {
                rules[0][0].replace('-', '_'): rules[0][1] for rules in
                rule_descriptors.get('font_variant', [])}
            settings = rule_descriptors.get('font_feature_settings', 'normal')
            features = font_features(font_feature_settings=settings, **descriptors)
            for key, value in features.items():
                SubElement(edit, 'string').text = f'{key} {value}'
            header = (
                b'<?xml version="1.0"?>',
                b'<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">')
            xml = b'\n'.join((*header, tostring(root, encoding='utf-8')))

            # Register font and configuration in Fontconfig.
            # TODO: We should mask local fonts with the same name
            # too as explained in Behdad's blog entry.
            fontconfig.FcConfigParseAndLoadFromMemory(self._config, xml, True)
            font_added = fontconfig.FcConfigAppFontAddFile(
                self._config, str(font_path).encode(FILESYSTEM_ENCODING))
            if font_added:
                return pangoft2.pango_fc_font_map_config_changed(
                    ffi.cast('PangoFcFontMap *', self.font_map))
            LOGGER.debug('Failed to load font at %r', url)
        LOGGER.warning('Font-face %r cannot be loaded', rule_descriptors['font_family'])
Add a font face to the Fontconfig configuration.
weasyprint/text/fonts.py