HomeAboutPostsTagsProjectsRSS

Updated
Words939
TagsRead1 minute

中文字体的挑战

与只含几十个字母加上常用符号的拉丁字体不同,一个完整的中文字体字符集超过 1 万个字符。中文常用字有 3500+个,而《通用规范汉字表》 则收录了 8000 多个汉字。因此中文字体的大小远超英文字体,往往从 10MB起步1。这意味着中文字体大多预装在操作系统内,而不适宜作为网站资源直接加载。

可变字体( Variable Fonts )

单一常规字体的体积已经相当可观,如果还需要支持粗体、斜体等不同字形(如Bold Italic),每种字形又得加载独立的字体文件,总字体文件的体积就会成倍增加。因此, variable fonts 技术应运而生,它通过算法将多种字形融合进一个文件中,减少了总体积,尽管总体积会略大于单个字体文件。例如, Adobe 在其 Source Han Sans 字体的介绍中说明了一些技术细节,最终能以 4.2MB 的 woff2 格式提供可变字体。不过,即使如此,在网页中直接引用一个 4.2MB 的字体文件也会显著影响网页加载速度。

解决方案

目前针对网页中使用中文字体的最佳实践是,将中文字体分割成多个包,并利用CSS中的unicode-range特性来实现字符的按需加载。这样,只有在网页上出现需要渲染的中文字时,才会加载对应的资源包。通常情况下,只需加载几个包含常用字符的字体包。

The unicode-range CSS descriptor sets the specific range of characters to be used from a font defined by @font-face and made available for use on the current page. If the page doesn’t use any character in this range, the font is not downloaded; if it uses at least one, the whole font is downloaded.

MDN Web Docs

使用 local google fonts 在线工具可以轻松实现字体拆分和生成 CSS 引用。下载 CSS文件后,请确保修改src: url()以使用正确的路径。

CSS 配置

有了正确的 font-face 和中文字体之后,应该在 CSS 中设置中文字体的 fallback, 使用Noto Sans SC添加比较通用的 fallback 字体设置如下

font-family: 'Noto Sans SC', "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;

References


  1. 谷歌推出的 Noto Sans SC 简体中文字体大小超过 10MB ( ttf格式)。 ↩︎

Updated
Words199
TagsRead1 minute

In Emacs, the fill-paragraph function neatly reflows a block of text, wrapping lines to fit within a specified column width. VS Code offers an extension called “Rewrap,” which you can install to enjoy similar functionality.

  • Selecting the text you want to rewrap and pressing Alt+Q, or
  • Placing your cursor within a comment block and pressing Alt+Q to rewrap that comment.

This feature can increase your productivity significantly if you’re editing or writing a considerable amount of text. It streamlines the process of formatting paragraphs, ensuring that they look neat and are easier to read, very much like Emacs’ fill-paragraph.

Rewrap - Visual Studio Marketplace

Updated
Words1776
TagsRead3 minutes

I recently ventured into deploying a service on Google Cloud Run. My goal was straightforward: create a service that fetches webpage titles and captures screenshots of URLs. However, the journey led me into a peculiar bug when I actually used it on Goole Cloud Run.

The Bug

During the development phase, I worked with a python:3.11-slim base image on macOS, and my Dockerfile functioned without a hitch. Here’s a snapshot of the Dockerfile I used:

from python:3.11-slim

RUN apt-get update && \
    apt-get install -y git && \
    python -m pip install --upgrade pip && \
    pip install -r requirements.txt && \
    pip install pytest-playwright && \
    playwright install-deps && \
    playwright install && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

Yet, upon deploying to Google Cloud Run and initiating the screenshot capture process, I hit a snag:

playwright._impl._api_types.Error: Executable doesn't exist at /home/.cache/ms-playwright/chromium-1084/chrome-linux/chrome
╔═════════════════════════════
║ Looks like Playwright was just installed or updated.                   
║ Please run the following command to download new browsers: 
║     playwright install                                                                          
║ <3 Playwright Team                                                                         
╚═════════════════════════════

Official Playwright Docker Image Saves the Day

Rather than wrestle with the error, I pivoted to an official Docker image of Playwright, and skipped installation of dependency:

mcr.microsoft.com/playwright/python:v1.39.0-jammy

docker image

Let’s dig down the issue:

The Compatibility Issue

Playwright demands compatibility. It officially supports Python versions 3.8 and higher, and it requires specific Linux distributions:

  • Debian 11 (bullseye)
  • Debian 12 (bookworm)
  • Ubuntu 20.04 (focal)
  • Ubuntu 22.04 (jammy)

However, on docker environment, the official image is only based on Unbuntu .

Use ENV PLAYWRIGHT_BROWSERS_PATH

After some search and experiments, I found the only solution in to specify the chromium binary files using ENV PLAYWRIGHT_BROWSERS_PATH during install . source code Dockerfile also use this environment variable to specify the broswer executable path.

using python:3.11-slim-bookworm

FROM python:3.11-slim-bookworm

ENV PLAYWRIGHT_BROWSERS_PATH=/app/ms-playwright
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1

# Install git
RUN apt-get update

# install playwright or whatever
RUN python -m pip install --upgrade pip && \
    pip install -r requirements.txt

# install chrominum
RUN PLAYWRIGHT_BROWSERS_PATH=/app/ms-playwright && \
    playwright install --with-deps chromium

using ubuntu:22.04

FROM ubuntu:22.04

ENV PLAYWRIGHT_BROWSERS_PATH=/app/ms-playwright
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1

# Install git
RUN apt-get update && \
    apt-get install -y python3-all python-is-python3 python3-pip

# install playwright or whatever
RUN python -m pip install --upgrade pip && \
    pip install -r requirements.txt

# install chrominum
RUN PLAYWRIGHT_BROWSERS_PATH=/app/ms-playwright && \
    playwright install --with-deps chromium

Memory requirement of Google Cloud Run

The playwright 1.39.0 requires slightly more than 512MB of memory to run on Google Cloud Run. Adjust the memory limit on GCR, as it’s 512 MB by default.

Conclusion

Use the official Docker image to save time, or specify the PLAYWRIGHT_BROWSERS_PATH environment variable on a supported linux docker image.

Further reading:

Updated
Words204
TagsRead1 minute

This tool Eruda: Console for Mobile Browsers is handy for examining HTML elements on mobile devices without any installation. Simply copy and paste the code below into the address bar, an icon will show up on the screen.

javascript:(function () { var script = document.createElement('script'); script.src="[](https://cdn.jsdelivr.net/npm/eruda";) document.body.append(script); script.onload = function () { eruda.init(); } })();

Updated
Words603
TagsRead1 minute

RoutineHub • Elevate Mobile Coding Together with Apple Shortcuts amazing resources for iOS shortcuts, one thing particularly useful is that the website can browse by App. Those shortcuts that use Scriptable on the App Store and a-Shell mini on the App Store Truly elevate automation capabilities on iOS to a new level. By combining these tools, you can accomplish complex tasks entirely on your iPhone.

a-shell notably supports Python, which is relevant given Python’s ongoing discussions about adding Tier 3 support for the iOS platform in version 3.13. PEP 730 .

The focus is on “embedded mode”, since there are no stdout on iOS and you can’t provide things like the Python REPL

What’s up Python? iOS support, ruff gets black, flask 3.0…

Updated
Words76
TagsRead1 minute

When using the a-shell action in iOS Shortcuts to execute shell commands or Python scripts, it is often necessary to wait for the execution to finish before continuing to execute other actions.

This can be achieved by using the open shortcuts:// command as the last step in the a-shell action and adding a Wait for Return action afterwards. This combination allows for waiting until a-shell finishes executing a long-running process before proceeding with other actions.

Updated
Words714
TagsRead3 minutes

I use this prompt to study German. Whenever I encounter a new German word, I ask ChatGPT to explain it to me. This prompt works particularly well with GPT-4.

When I enter a German word, your task is to provide a comprehensive analysis of that word. The analysis includes the following segments:

1. Meaning and Usage: Explanation of the word's meaning and common usage scenarios.
2. Linguistic Analysis: Analysis of the word's structure including any prefix, root, and suffix, and its etymology if applicable.
3. Comparisons between German and English: Highlighting any similarities or differences between the German word and its English counterpart.
4. Cultural Context: Any pertinent cultural context related to the word, if applicable.
5. Example Sentences: Providing sentences to illustrate the word's usage.
6. Memory Tips: Suggestions for remembering the word's meaning and usage.
7. Additional Vocabulary: Providing related words, synonyms, or antonyms to broaden vocabulary knowledge.
8. Gender and Plural (for nouns): Indicating the gender of the noun and its plural form, if applicable.
9. Conjugation (for verbs): Present tense conjugation, if applicable.

Refer to this format to provide a thorough understanding of the inquired German word, aiding in learning and retention of the word and related vocabulary.

Here is an example.

Input: Anzeige

The German noun "Anzeige" translates to "advertisement," "display," or "notification" in English, depending on the context. It's often used to refer to advertising materials, displays on devices, or formal notifications, such as reporting a crime. Here’s a detailed analysis of the word:

### 1. **Meaning and Usage:**
   - **Advertisement:** "Anzeige" can refer to an advertisement in a newspaper, magazine, or online platform.
   - **Display:** It can also denote a display on devices such as computers, phones, or other electronic gadgets.
   - **Notification/Report:** In a legal or formal context, "Anzeige" can refer to a notification or a report, often related to reporting incidents to authorities.

### 2. **Linguistic Analysis:**
   - Compound Word: "Anzeige" is derived from "anzeigen," a verb which means "to display," "to indicate," or "to report."

### 3. **Gender:**
   - "Anzeige" is a feminine noun, thus it is "die Anzeige."

### 4. **Comparisons between German and English:**
   - The multiple meanings of "Anzeige" in German align with various English terms such as "advertisement," "display," and "notification" based on context.

### 5. **Cultural Context:**
   - "Anzeige" is a common term used in various contexts in German-speaking countries, whether it's in advertising, technology, or legal realms.

### 6. **Example Sentences:**
   - Ich habe eine Anzeige in der Zeitung geschaltet. (I placed an advertisement in the newspaper.)
   - Die Anzeige des Telefons zeigt einen niedrigen Batteriestand. (The display of the phone shows a low battery level.)
   - Er hat eine Anzeige gegen den Dieb erstattet. (He filed a report against the thief.)

### 7. **Memory Tips:**
   - Associating "Anzeige" with visuals or formal notifications can help in remembering its various meanings.
   - Relating "Anzeige" to its verb form "anzeigen" (to display, to indicate, to report) can also serve as a mnemonic aid.

### 8. **Additional Vocabulary:**
   - Die Werbung (the advertising, feminine): A general term for advertising.
   - Der Bildschirm (the screen, masculine): Referring to the display screen of electronic devices.
   - Die Meldung (the report, notification, feminine): A term often used for notifications or reports.

This analysis should provide a thorough understanding of "Anzeige" and its usage in German. If there are any further inquiries or if you need more examples, feel free to ask!

Updated
Words791
TagsRead2 minutes

Introduction

In a Django application deployed with gunicorn, it is important to optimize the handling of database connections to improve performance and efficiency. This post explores the behavior of gunicorn in relation to database connections and provides insights on how to reuse connections effectively.

Django + Gunicorn

Improtant note about Django ORM is that Django ORM does not have a build-int database connection pool like SQLAlchemy. The lifetime of the persistant connections are controlled by the CONN_MAX_AGE , the default value is 0.

The Sync Worker Class

In the case where CONN_MAX_AGE is set to 0, a new database connection is created for every HTTP request and closed at the end of each request. This can potentially lead to increased overhead due to the constant creation and closure of connections.

However, if CONN_MAX_AGE is set to a value greater than 0, the database connection is not closed immediately after the request is finished. In gunicorn’s sync worker class, which is process-based with a fixed number of processes, subsequent requests will be processed by the same processes, keeping the number of database connections stable.

The Async Worker Class: eventlet and gevent

For the default case where CONN_MAX_AGE is 0, there are no issues as connections are closed at the end of each request.

However, if CONN_MAX_AGE is not 0, problems can arise. In gunicorn’s async worker class, such as eventlet and gevent, each new HTTP request creates a new greenlet thread to handle it. Each thread maintains its own connection, leading to an accumulation of database connections over time.

To tackle this issue, the official gunicorn documentation recommends using the gevent worker and activating greenlet support for Psycopg2 when connecting to PostgreSQL. This can be achieved using the psycogreen library.

References

For more information, refer to the following resources:

Updated
Words835
TagsRead1 minute

Overview

This guide provides step-by-step instructions for installing and enabling content caching for a macOS virtual machine (VM) in Proxmox.

Install a macOS VM

Follow the tutorial here to install a macOS VM in Proxmox.

Enable Content Caching

If you encounter difficulties enabling content caching through the Sharing menu, follow the instructions provided here .

Registration

If you are unable to register and receive a “Content Caching is temporarily unavailable” message, follow these steps:

  1. Post-install OpenCore configuration.
  2. Fix GenSMBios by following the steps here .
  3. Fix en0 by verifying en0 as build-in. Details can be found here .

Image: en0

  1. Fix ROM by following the steps here .

  2. Reboot your VM and verify the content caching status.

    Reboot and verify the content caching status, you should be seen Activated: true and Active: true

> AssetCacheManagerUtil status

Content caching status:
    Activated: true
    Active: true
    ActualCacheUsed: 2.79 GB
    CacheDetails: (3)
        iCloud: 23.86 GB
        Mac Software: 211.7 MB
        Other: 701.7 MB
    CacheFree: 14.28 GB
    CacheLimit: 45 GB
    CacheStatus: OK
    CacheUsed: 24.77 GB

Image: Content Caching Status

For additional information and discussion, refer to the Reddit post .