~cytrogen/.emacs.d

ref: 8c864d76e33eaf0414695f33fd3ec4a5d3fec1c4 .emacs.d/config/ui-theme-fonts.el -rw-r--r-- 12.1 KiB
8c864d76 — Cytrogen feat(core): 添加补全框架、会话管理和编辑增强 a month ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
;;; ui-theme-fonts.el --- User interface, themes, and font configuration -*- lexical-binding: t -*-

;; Copyright (C) 2024 Cytrogen

;; This file contains:
;; - Font configuration with CJK support
;; - Theme loading and customization
;; - Visual enhancements

;;; Commentary:

;; Comprehensive font setup supporting English, Chinese, and special symbols.
;; Includes theme configuration and visual mode customizations.

;;; Code:

;; Font Configuration
;; Font fallback system for cross-platform compatibility
(defvar my/font-fallback-alist
  '(;; CJK Sans fonts (思源黑体)
    ("Source Han Sans SC" . ("Source Han Sans CN" "Noto Sans CJK SC"))
    ;; CJK Serif fonts (思源宋体)
    ("思源宋体 CN" . ("Source Han Serif CN" "Noto Serif CJK SC"))
    ;; CJK Sans for headings
    ("Noto Sans SC" . ("Noto Sans CJK SC" "Source Han Sans CN"))
    ;; Nerd Font icons
    ("Symbols Nerd Font Mono" . ("Symbols Nerd Font"))
    ;; Emoji fonts
    ("Segoe UI Emoji" . ("Noto Color Emoji" "Symbola"))
    ("Apple Color Emoji" . ("Noto Color Emoji" "Symbola")))
  "Alist mapping preferred font names to fallback alternatives.
Each entry is (PREFERRED-FONT . (FALLBACK1 FALLBACK2 ...)).")

(defun my/find-available-font (preferred &optional fallbacks)
  "Return PREFERRED font if available, otherwise try FALLBACKS in order.
If FALLBACKS is nil, look up PREFERRED in `my/font-fallback-alist'.
Returns the first available font name, or nil if none found."
  (let ((candidates (cons preferred
                          (or fallbacks
                              (cdr (assoc preferred my/font-fallback-alist))))))
    (cl-find-if (lambda (f) (find-font (font-spec :family f))) candidates)))

;; Advanced multi-script font setup
(defun my/setup-blog-fonts ()
  "Setup fonts with precise control over CJK, Punctuation, and Nerd Icons.
Refactored to strictly separate Fixed Pitch (Code) and Variable Pitch (Writing) faces."
  (interactive)
  (require 'org)
  
  ;; Skip font setup in batch mode or when no display
  (if (or (not (display-graphic-p)) 
          (not (fboundp 'find-font)))
      (message "Skipping font setup in non-graphic environment")
    
    (message "Setting up fonts in graphic environment...")
    
    (set-char-table-range char-script-table '(#x3000 . #x303F) 'han)
    (set-char-table-range char-script-table '(#xFF00 . #xFFEF) 'han)
    (set-char-table-range char-script-table '(#x2000 . #x206F) 'han)

    ;; 英文/数字: 强制 JetBrains Mono
    (set-face-attribute 'default nil :font "JetBrains Mono-13")
    
    ;; 中文: 强制 Source Han Sans SC (思源黑体) 用于 UI 和基础对齐
    (let ((cjk-sans (my/find-available-font "Source Han Sans SC")))
      (when cjk-sans
        (set-fontset-font t 'han (font-spec :family cjk-sans))
        (set-fontset-font t 'cjk-misc (font-spec :family cjk-sans))))

    ;; 专门用于代码块、表格、属性栏
    (set-face-attribute 'fixed-pitch nil :family "JetBrains Mono" :height 1.0)
    
    ;; 为 fixed-pitch 补充中文部分,保持表格对齐
    (let ((fixed-fontset (face-attribute 'fixed-pitch :fontset))
          (default-font (face-attribute 'default :font))
          (cjk-sans (my/find-available-font "Source Han Sans SC")))
      (when (eq fixed-fontset 'unspecified)
        (when (and default-font (not (eq default-font 'unspecified)))
          (setq fixed-fontset (create-fontset-from-fontset-spec
                               (font-xlfd-name default-font)))))
      (when (and fixed-fontset cjk-sans)
        (set-fontset-font fixed-fontset 'han (font-spec :family cjk-sans))))

    ;; 英文(变宽): 优先使用 Source Serif 4,如果没有则回退到 Georgia
    (let* ((en-serif-font (if (find-font (font-spec :family "Source Serif 4"))
                              "Source Serif 4"
                            "Georgia"))
           ;; 创建一个专属的 fontset,命名为 fontset-variable
           (v-fontset-name "fontset-variable"))
      
      ;; 安全地创建 fontset
      (condition-case err
          (let ((v-fontset (create-fontset-from-fontset-spec
                            (font-xlfd-name
                             (font-spec :family en-serif-font
                                        :registry "fontset-variable")))))
            
            ;; 强制指定 slant 为 normal,防止英文也跟着变斜
            (set-face-attribute 'variable-pitch nil 
                                :family en-serif-font 
                                :height 1.1 
                                :weight 'regular
                                :slant 'normal
                                :fontset v-fontset-name)

            (let ((cjk-serif-name (my/find-available-font "思源宋体 CN")))
              (if cjk-serif-name
                  (progn
                    ;; 针对汉字 (han)
                    (set-fontset-font v-fontset-name 'han
                                      (font-spec :family cjk-serif-name
                                                 :weight 'normal
                                                 :slant 'normal)) ;; 关键:显式禁止倾斜

                    ;; 针对中文标点 (#x3000 - #x303F)
                    (set-fontset-font v-fontset-name '(#x3000 . #x303F)
                                      (font-spec :family cjk-serif-name
                                                 :weight 'normal
                                                 :slant 'normal)))
                (message "Warning: No CJK Serif font found (tried 思源宋体 CN and fallbacks)."))))
        (error
         (message "Warning: Variable pitch fontset creation failed: %s" (error-message-string err))
         ;; Fallback to simple variable-pitch setup
         (set-face-attribute 'variable-pitch nil 
                             :family en-serif-font 
                             :height 1.1 
                             :weight 'regular
                             :slant 'normal))))

    (let ((heading-font-family "Open Sans")
          (heading-cjk-family (my/find-available-font "Noto Sans SC")))
      (when (find-font (font-spec :family heading-font-family))
        (dolist (face '(org-level-1 org-level-2 org-level-3 org-level-4
                        org-level-5 org-level-6 org-level-7 org-level-8))
          (when (facep face)
            (set-face-attribute face nil :family heading-font-family :weight 'bold)
            ;; 确保标题中文使用黑体
            (when heading-cjk-family
              (let ((face-fontset (face-attribute face :fontset))
                    (default-font (face-attribute 'default :font)))
                (when (eq face-fontset 'unspecified)
                   ;; 如果 face 没有独立的 fontset,创建一个
                   (when (and default-font (not (eq default-font 'unspecified)))
                     (setq face-fontset (create-fontset-from-fontset-spec
                                         (font-xlfd-name default-font)))))
                (when face-fontset
                  (set-fontset-font face-fontset 'han
                                    (font-spec :family heading-cjk-family :weight 'bold)))))))))

    ;; 让 Org 的代码块、元数据、表格、引用块强制继承 fixed-pitch
    (dolist (face '(org-block org-block-begin-line org-block-end-line
                    org-code org-table org-verbatim org-formula
                    org-checkbox org-date org-priority org-special-keyword
                    org-tag line-number))
      (when (facep face)
        (set-face-attribute face nil :inherit 'fixed-pitch)))

    ;; 让语法高亮 faces 在 variable-pitch-mode 下保持等宽
    ;; 解决 org src block 内 defun/setq 等关键字显示为 serif 的问题
    (dolist (face '(font-lock-comment-face
                    font-lock-comment-delimiter-face
                    font-lock-string-face
                    font-lock-doc-face
                    font-lock-keyword-face
                    font-lock-builtin-face
                    font-lock-function-name-face
                    font-lock-variable-name-face
                    font-lock-type-face
                    font-lock-constant-face
                    font-lock-warning-face
                    font-lock-negation-char-face
                    font-lock-preprocessor-face
                    font-lock-regexp-grouping-backslash
                    font-lock-regexp-grouping-construct))
      (when (facep face)
        (set-face-attribute face nil :inherit 'fixed-pitch)))

    ;; 特殊处理:Meta line (如 #+TITLE) 通常需要更淡的颜色,但也需要等宽
    (when (facep 'org-meta-line)
      (set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch)))

    (let ((nerd-font-name (my/find-available-font "Symbols Nerd Font Mono")))
      (when nerd-font-name
        (set-fontset-font t '(#xE000 . #xF8FF)
                          (font-spec :family nerd-font-name) nil 'prepend)))

    ;; Emoji 字体配置 (跨平台)
    (let* ((emoji-candidates
            (if (eq system-type 'windows-nt)
                '("Segoe UI Emoji" "Noto Color Emoji" "Symbola" "Apple Color Emoji")
              '("Noto Color Emoji" "Symbola" "Noto Emoji")))
           (primary-emoji-font (cl-find-if (lambda (f) (find-font (font-spec :family f))) emoji-candidates)))
      (if primary-emoji-font
          (let ((emoji-spec (font-spec :family primary-emoji-font)))
            (set-fontset-font t 'emoji emoji-spec nil 'prepend)
            (set-fontset-font t '(#x1F000 . #x1FFFF) emoji-spec nil 'prepend)
            (set-fontset-font t '(#x2600 . #x27BF) emoji-spec nil 'prepend))
        (message "Warning: No suitable Emoji font found.")))

    (message "Fonts setup: Default(JB Mono) | Writing(Source Serif 4/Georgia) | Fixed(JB Mono)")

    ;; 设置代码块和行内代码的背景色
    (my/setup-org-code-faces)))

(defun my/setup-org-code-faces ()
  "Setup org-mode code block and inline code faces with theme-adaptive colors."
  (let* ((bg (face-background 'default))
         (is-dark (< (apply '+ (color-values bg)) (* 3 32768)))
         ;; 代码块比正文背景更深
         (block-bg (if is-dark "#1e1e1e" "#e8e8e8"))
         (block-border-bg (if is-dark "#151515" "#d8d8d8"))
         (block-border-fg (if is-dark "#606060" "#707070"))
         (inline-bg (if is-dark "#2a2a2a" "#e0e0e0"))
         (inline-fg (if is-dark "#e06c75" "#c7254e")))

    ;; 使用 face-spec-set 直接设置 face,不影响其他设置
    (face-spec-set 'org-block
                   `((t (:background ,block-bg :extend t :inherit fixed-pitch))))
    (face-spec-set 'org-block-begin-line
                   `((t (:background ,block-border-bg :foreground ,block-border-fg :extend t :inherit fixed-pitch))))
    (face-spec-set 'org-block-end-line
                   `((t (:background ,block-border-bg :foreground ,block-border-fg :extend t :inherit fixed-pitch))))
    (face-spec-set 'org-code
                   `((t (:background ,inline-bg :foreground ,inline-fg :inherit fixed-pitch))))
    (face-spec-set 'org-verbatim
                   `((t (:background ,inline-bg :foreground ,inline-fg :inherit fixed-pitch))))))

(defun my/force-restore-fonts (&rest _args)
  (message "Theme changed, re-applying fonts...")
  (my/setup-blog-fonts))

;; Theme Configuration
;; Custom theme loading and adjustments
(add-to-list 'custom-theme-load-path "~/.emacs.d/themes/")
(load-theme 'darcula t)

;; Font restoration after theme changes
(advice-add 'load-theme :after #'my/force-restore-fonts)

;; Visual Enhancements
;; Line wrapping and writing modes
(add-hook 'org-mode-hook #'visual-line-mode)
(add-hook 'markdown-mode-hook #'visual-line-mode)

(when (fboundp 'adaptive-wrap-prefix-mode)
  (add-hook 'visual-line-mode-hook #'adaptive-wrap-prefix-mode))

(defun my/org-mode-visual-setup ()
  "Activate visual adjustments for Org mode writing."
  (variable-pitch-mode 1)  ; 开启变宽字体模式(正文使用 Source Serif 4/宋体)
  (visual-line-mode 1))    ; 开启视觉折行(按词换行,而非按字符截断)

(add-hook 'org-mode-hook 'my/org-mode-visual-setup)

;; Apply fonts on startup
(if (daemonp)
    (add-hook 'after-make-frame-functions 
              (lambda (frame) (with-selected-frame
				  frame (my/setup-blog-fonts))))
  (my/setup-blog-fonts))

(provide 'ui-theme-fonts)
;;; ui-theme-fonts.el ends here