diff options
| author | 2024-01-25 17:46:34 +0800 | |
|---|---|---|
| committer | 2024-01-25 17:46:34 +0800 | |
| commit | c4eb3ae7a74c7c6881540d7d04502600197a8b2a (patch) | |
| tree | c920c29c383da4d496a0f646e5d4d0f5de10a406 /docs/src/plugins/social | |
| parent | 3c3e121bc845573a2d2408c0db6be6f841fd8efc (diff) | |
| download | infini-c4eb3ae7a74c7c6881540d7d04502600197a8b2a.tar.gz infini-c4eb3ae7a74c7c6881540d7d04502600197a8b2a.zip | |
:fire: feat(docs): remove old documents
Diffstat (limited to 'docs/src/plugins/social')
| -rw-r--r-- | docs/src/plugins/social/__init__.py | 19 | ||||
| -rw-r--r-- | docs/src/plugins/social/config.py | 48 | ||||
| -rw-r--r-- | docs/src/plugins/social/plugin.py | 516 |
3 files changed, 0 insertions, 583 deletions
diff --git a/docs/src/plugins/social/__init__.py b/docs/src/plugins/social/__init__.py deleted file mode 100644 index d1899378..00000000 --- a/docs/src/plugins/social/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com> - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. diff --git a/docs/src/plugins/social/config.py b/docs/src/plugins/social/config.py deleted file mode 100644 index 2d87c25e..00000000 --- a/docs/src/plugins/social/config.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com> - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -from mkdocs.config.base import Config -from mkdocs.config.config_options import Deprecated, Type - -# ----------------------------------------------------------------------------- -# Classes -# ----------------------------------------------------------------------------- - -# Social plugin configuration -class SocialConfig(Config): - enabled = Type(bool, default = True) - cache_dir = Type(str, default = ".cache/plugin/social") - - # Settings for social cards - cards = Type(bool, default = True) - cards_dir = Type(str, default = "assets/images/social") - cards_layout_options = Type(dict, default = {}) - - # Deprecated settings - cards_color = Deprecated( - option_type = Type(dict, default = {}), - message = - "Deprecated, use 'cards_layout_options.background_color' " - "and 'cards_layout_options.color' with 'default' layout" - ) - cards_font = Deprecated( - option_type = Type(str), - message = "Deprecated, use 'cards_layout_options.font_family'" - ) diff --git a/docs/src/plugins/social/plugin.py b/docs/src/plugins/social/plugin.py deleted file mode 100644 index 3cdfa3ce..00000000 --- a/docs/src/plugins/social/plugin.py +++ /dev/null @@ -1,516 +0,0 @@ -# Copyright (c) 2016-2023 Martin Donath <martin.donath@squidfunk.com> - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -# ----------------------------------------------------------------------------- -# Disclaimer -# ----------------------------------------------------------------------------- -# Please note: this version of the social plugin is not actively development -# anymore. Instead, Material for MkDocs Insiders ships a complete rewrite of -# the plugin which is much more powerful and addresses all shortcomings of -# this implementation. Additionally, the new social plugin allows to create -# entirely custom social cards. You can probably imagine, that this was a lot -# of work to pull off. If you run into problems, or want to have additional -# functionality, please consider sponsoring the project. You can then use the -# new version of the plugin immediately. -# ----------------------------------------------------------------------------- - -import concurrent.futures -import functools -import logging -import os -import posixpath -import re -import requests -import sys - -from collections import defaultdict -from hashlib import md5 -from io import BytesIO -from mkdocs.commands.build import DuplicateFilter -from mkdocs.exceptions import PluginError -from mkdocs.plugins import BasePlugin -from shutil import copyfile -from tempfile import TemporaryFile -from zipfile import ZipFile -try: - from cairosvg import svg2png - from PIL import Image, ImageDraw, ImageFont -except ImportError: - pass - -from .config import SocialConfig - - -# ----------------------------------------------------------------------------- -# Classes -# ----------------------------------------------------------------------------- - -# Social plugin -class SocialPlugin(BasePlugin[SocialConfig]): - - def __init__(self): - self._executor = concurrent.futures.ThreadPoolExecutor(4) - - # Retrieve configuration - def on_config(self, config): - self.color = colors.get("indigo") - self.config.cards = self.config.enabled - if not self.config.cards: - return - - # Check dependencies - if "Image" not in globals(): - raise PluginError( - "Required dependencies of \"social\" plugin not found. " - "Install with: pip install \"mkdocs-material[imaging]\"" - ) - - # Move color options - if self.config.cards_color: - - # Move background color to new option - value = self.config.cards_color.get("fill") - if value: - self.config.cards_layout_options["background_color"] = value - - # Move color to new option - value = self.config.cards_color.get("text") - if value: - self.config.cards_layout_options["color"] = value - - # Move font family to new option - if self.config.cards_font: - value = self.config.cards_font - self.config.cards_layout_options["font_family"] = value - - # Check if site URL is defined - if not config.site_url: - log.warning( - "The \"site_url\" option is not set. The cards are generated, " - "but not linked, so they won't be visible on social media." - ) - - # Ensure presence of cache directory - self.cache = self.config.cache_dir - if not os.path.isdir(self.cache): - os.makedirs(self.cache) - - # Retrieve palette from theme configuration - theme = config.theme - if "palette" in theme: - palette = theme["palette"] - - # Use first palette, if multiple are defined - if isinstance(palette, list): - palette = palette[0] - - # Set colors according to palette - if "primary" in palette and palette["primary"]: - primary = palette["primary"].replace(" ", "-") - self.color = colors.get(primary, self.color) - - # Retrieve color overrides - options = self.config.cards_layout_options - self.color = { - "fill": options.get("background_color", self.color["fill"]), - "text": options.get("color", self.color["text"]) - } - - # Retrieve logo and font - self._resized_logo_promise = self._executor.submit(self._load_resized_logo, config) - self.font = self._load_font(config) - - self._image_promises = [] - - # Create social cards - def on_page_markdown(self, markdown, page, config, files): - if not self.config.cards: - return - - # Resolve image directory - directory = self.config.cards_dir - file, _ = os.path.splitext(page.file.src_path) - - # Resolve path of image - path = "{}.png".format(os.path.join( - config.site_dir, - directory, - file - )) - - # Resolve path of image directory - directory = os.path.dirname(path) - if not os.path.isdir(directory): - os.makedirs(directory) - - # Compute site name - site_name = config.site_name - - # Compute page title and description - title = page.meta.get("title", page.title) - description = config.site_description or "" - if "description" in page.meta: - description = page.meta["description"] - - # Check type of meta title - see https://t.ly/m1Us - if not isinstance(title, str): - log.error( - f"Page meta title of page '{page.file.src_uri}' must be a " - f"string, but is of type \"{type(title)}\"." - ) - sys.exit(1) - - # Check type of meta description - see https://t.ly/m1Us - if not isinstance(description, str): - log.error( - f"Page meta description of '{page.file.src_uri}' must be a " - f"string, but is of type \"{type(description)}\"." - ) - sys.exit(1) - - # Generate social card if not in cache - hash = md5("".join([ - site_name, - str(title), - description - ]).encode("utf-8")) - file = os.path.join(self.cache, f"{hash.hexdigest()}.png") - self._image_promises.append(self._executor.submit( - self._cache_image, - cache_path = file, dest_path = path, - render_function = lambda: self._render_card(site_name, title, description) - )) - - # Inject meta tags into page - meta = page.meta.get("meta", []) - page.meta["meta"] = meta + self._generate_meta(page, config) - - def on_post_build(self, config): - if not self.config.cards: - return - - # Check for exceptions - for promise in self._image_promises: - promise.result() - - # ------------------------------------------------------------------------- - - # Render image to cache (if not present), then copy from cache to site - def _cache_image(self, cache_path, dest_path, render_function): - if not os.path.isfile(cache_path): - image = render_function() - image.save(cache_path) - - # Copy file from cache - copyfile(cache_path, dest_path) - - @functools.lru_cache(maxsize=None) - def _get_font(self, kind, size): - return ImageFont.truetype(self.font[kind], size) - - # Render social card - def _render_card(self, site_name, title, description): - # Render background and logo - image = self._render_card_background((1200, 630), self.color["fill"]) - image.alpha_composite( - self._resized_logo_promise.result(), - (1200 - 228, 64 - 4) - ) - - # Render site name - font = self._get_font("Bold", 36) - image.alpha_composite( - self._render_text((826, 48), font, site_name, 1, 20), - (64 + 4, 64) - ) - - # Render page title - font = self._get_font("Bold", 92) - image.alpha_composite( - self._render_text((826, 328), font, title, 3, 30), - (64, 160) - ) - - # Render page description - font = self._get_font("Regular", 28) - image.alpha_composite( - self._render_text((826, 80), font, description, 2, 14), - (64 + 4, 512) - ) - - # Return social card image - return image - - # Render social card background - def _render_card_background(self, size, fill): - return Image.new(mode = "RGBA", size = size, color = fill) - - @functools.lru_cache(maxsize=None) - def _tmp_context(self): - image = Image.new(mode = "RGBA", size = (50, 50)) - return ImageDraw.Draw(image) - - @functools.lru_cache(maxsize=None) - def _text_bounding_box(self, text, font): - return self._tmp_context().textbbox((0, 0), text, font = font) - - # Render social card text - def _render_text(self, size, font, text, lmax, spacing = 0): - width = size[0] - lines, words = [], [] - - # Remove remnant HTML tags - text = re.sub(r"(<[^>]+>)", "", text) - - # Retrieve y-offset of textbox to correct for spacing - yoffset = 0 - - # Create drawing context and split text into lines - for word in text.split(" "): - combine = " ".join(words + [word]) - textbox = self._text_bounding_box(combine, font = font) - yoffset = textbox[1] - if not words or textbox[2] <= width: - words.append(word) - else: - lines.append(words) - words = [word] - - # Join words for each line and create image - lines.append(words) - lines = [" ".join(line) for line in lines] - image = Image.new(mode = "RGBA", size = size) - - # Create drawing context and split text into lines - context = ImageDraw.Draw(image) - context.text( - (0, spacing / 2 - yoffset), "\n".join(lines[:lmax]), - font = font, fill = self.color["text"], spacing = spacing - yoffset - ) - - # Return text image - return image - - # ------------------------------------------------------------------------- - - # Generate meta tags - def _generate_meta(self, page, config): - directory = self.config.cards_dir - file, _ = os.path.splitext(page.file.src_uri) - - # Compute page title - title = page.meta.get("title", page.title) - if not page.is_homepage: - title = f"{title} - {config.site_name}" - - # Compute page description - description = config.site_description - if "description" in page.meta: - description = page.meta["description"] - - # Resolve image URL - url = "{}.png".format(posixpath.join( - config.site_url or ".", - directory, - file - )) - - # Ensure forward slashes - url = url.replace(os.path.sep, "/") - - # Return meta tags - return [ - - # Meta tags for Open Graph - { "property": "og:type", "content": "website" }, - { "property": "og:title", "content": title }, - { "property": "og:description", "content": description }, - { "property": "og:image", "content": url }, - { "property": "og:image:type", "content": "image/png" }, - { "property": "og:image:width", "content": "1200" }, - { "property": "og:image:height", "content": "630" }, - { "property": "og:url", "content": page.canonical_url }, - - # Meta tags for Twitter - { "name": "twitter:card", "content": "summary_large_image" }, - # { "name": "twitter:site", "content": user }, - # { "name": "twitter:creator", "content": user }, - { "name": "twitter:title", "content": title }, - { "name": "twitter:description", "content": description }, - { "name": "twitter:image", "content": url } - ] - - def _load_resized_logo(self, config, width = 144): - logo = self._load_logo(config) - height = int(width * logo.height / logo.width) - return logo.resize((width, height)) - - # Retrieve logo image or icon - def _load_logo(self, config): - theme = config.theme - - # Handle images (precedence over icons) - if "logo" in theme: - _, extension = os.path.splitext(theme["logo"]) - - path = os.path.join(config.docs_dir, theme["logo"]) - - # Allow users to put the logo inside their custom_dir (theme["logo"] case) - if theme.custom_dir: - custom_dir_logo = os.path.join(theme.custom_dir, theme["logo"]) - if os.path.exists(custom_dir_logo): - path = custom_dir_logo - - # Load SVG and convert to PNG - if extension == ".svg": - return self._load_logo_svg(path) - - # Load PNG, JPEG, etc. - return Image.open(path).convert("RGBA") - - # Handle icons - icon = theme["icon"] or {} - if "logo" in icon and icon["logo"]: - logo = icon["logo"] - else: - logo = "material/library" - - # Resolve path of package - base = os.path.abspath(os.path.join( - os.path.dirname(__file__), - "../.." - )) - - path = f"{base}/templates/.icons/{logo}.svg" - - # Allow users to put the logo inside their custom_dir (theme["icon"]["logo"] case) - if theme.custom_dir: - custom_dir_logo = os.path.join(theme.custom_dir, ".icons", f"{logo}.svg") - if os.path.exists(custom_dir_logo): - path = custom_dir_logo - - # Load icon data and fill with color - return self._load_logo_svg(path, self.color["text"]) - - # Load SVG file and convert to PNG - def _load_logo_svg(self, path, fill = None): - file = BytesIO() - data = open(path).read() - - # Fill with color, if given - if fill: - data = data.replace("<svg", f"<svg fill=\"{fill}\"") - - # Convert to PNG and return image - svg2png(bytestring = data, write_to = file, scale = 10) - return Image.open(file) - - # Retrieve font - def _load_font(self, config): - name = self.config.cards_layout_options.get("font_family") - if not name: - - # Retrieve from theme (default: Roboto) - theme = config.theme - if isinstance(theme["font"], dict) and "text" in theme["font"]: - name = theme["font"]["text"] - else: - name = "Roboto" - - # Google fonts can return varients like OpenSans_Condensed-Regular.ttf so - # we only use the font requested e.g. OpenSans-Regular.ttf - font_filename_base = name.replace(' ', '') - filename_regex = re.escape(font_filename_base)+r"-(\w+)\.[ot]tf$" - - font = {} - # Check for cached files - note these may be in subfolders - for currentpath, folders, files in os.walk(self.cache): - for file in files: - # Map available font weights to file paths - fname = os.path.join(currentpath, file) - match = re.search(filename_regex, fname) - if match: - font[match.group(1)] = fname - - # If none found, fetch from Google and try again - if len(font) == 0: - self._load_font_from_google(name) - for currentpath, folders, files in os.walk(self.cache): - for file in files: - # Map available font weights to file paths - fname = os.path.join(currentpath, file) - match = re.search(filename_regex, fname) - if match: - font[match.group(1)] = fname - - # Return available font weights with fallback - return defaultdict(lambda: font["Regular"], font) - - # Retrieve font from Google Fonts - def _load_font_from_google(self, name): - url = "https://fonts.google.com/download?family={}" - res = requests.get(url.format(name.replace(" ", "+")), stream = True) - - # Write archive to temporary file - tmp = TemporaryFile() - for chunk in res.iter_content(chunk_size = 32768): - tmp.write(chunk) - - # Unzip fonts from temporary file - zip = ZipFile(tmp) - files = [file for file in zip.namelist() if file.endswith(".ttf") or file.endswith(".otf")] - zip.extractall(self.cache, files) - - # Close and delete temporary file - tmp.close() - return files - -# ----------------------------------------------------------------------------- -# Data -# ----------------------------------------------------------------------------- - -# Set up logging -log = logging.getLogger("mkdocs") -log.addFilter(DuplicateFilter()) - -# Color palette -colors = dict({ - "red": { "fill": "#ef5552", "text": "#ffffff" }, - "pink": { "fill": "#e92063", "text": "#ffffff" }, - "purple": { "fill": "#ab47bd", "text": "#ffffff" }, - "deep-purple": { "fill": "#7e56c2", "text": "#ffffff" }, - "indigo": { "fill": "#4051b5", "text": "#ffffff" }, - "blue": { "fill": "#2094f3", "text": "#ffffff" }, - "light-blue": { "fill": "#02a6f2", "text": "#ffffff" }, - "cyan": { "fill": "#00bdd6", "text": "#ffffff" }, - "teal": { "fill": "#009485", "text": "#ffffff" }, - "green": { "fill": "#4cae4f", "text": "#ffffff" }, - "light-green": { "fill": "#8bc34b", "text": "#ffffff" }, - "lime": { "fill": "#cbdc38", "text": "#000000" }, - "yellow": { "fill": "#ffec3d", "text": "#000000" }, - "amber": { "fill": "#ffc105", "text": "#000000" }, - "orange": { "fill": "#ffa724", "text": "#000000" }, - "deep-orange": { "fill": "#ff6e42", "text": "#ffffff" }, - "brown": { "fill": "#795649", "text": "#ffffff" }, - "grey": { "fill": "#757575", "text": "#ffffff" }, - "blue-grey": { "fill": "#546d78", "text": "#ffffff" }, - "black": { "fill": "#000000", "text": "#ffffff" }, - "white": { "fill": "#ffffff", "text": "#000000" } -}) |
