Some nice printable calendars to share.
at main 189 lines 6.7 kB view raw
1{ 2 description = "Entorno de desarrollo Typst"; 3 4 inputs = { 5 nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 6 flake-parts.url = "github:hercules-ci/flake-parts"; 7 }; 8 9 outputs = inputs @ {flake-parts, ...}: 10 flake-parts.lib.mkFlake {inherit inputs;} { 11 systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"]; 12 13 perSystem = { 14 config, 15 self', 16 inputs', 17 pkgs, 18 system, 19 ... 20 }: let 21 generateFontSpecimen = pkgs.writers.writePython3Bin "font-specimen" {} '' 22 import subprocess 23 import sys 24 from datetime import datetime 25 from collections import defaultdict 26 27 28 OUTPUT_TYP = "font-specimen.typ" 29 OUTPUT_PDF = "font-specimen.pdf" 30 PANGRAM = "The quick brown fox jumps over the lazy dog. 0123456789" 31 ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz" 32 DATE = datetime.now().strftime("%Y-%m-%d") 33 34 HEAVY_HITTERS = { 35 "Noto", "SF", "Apple", "Hiragino", "STIX", "CJK", 36 "DecoType", "FiraMono", "iMWritingMono" 37 } 38 39 40 def is_likely_latin(font_name): 41 # 1. Handle the Noto explosion: Only allow base Latin variants 42 if font_name.startswith("Noto "): 43 allowed = {"Noto Sans", "Noto Serif", "Noto Mono"} 44 base = font_name.replace(" UI", "") 45 if base not in allowed: 46 return False 47 48 if font_name.startswith("STIX"): 49 return False 50 51 # 2. Block known non-Latin scripts, math, emoji, and macOS fallbacks 52 blocked_terms = [ 53 "Arabic", "Hebrew", "CJK", "Devanagari", "Bangla", "Gurmukhi", 54 "Gujarati", "Oriya", "Tamil", "Telugu", "Kannada", "Malayalam", 55 "Sinhala", "Thai", "Lao", "Khmer", "Myanmar", "Armenian", 56 "Georgian", "Emoji", "Math", "Symbols", "Braille", "Gothic", 57 "Mincho", "Songti", "Heiti", "Kufi", "Naskh", "Nastaliq", 58 "Thuluth", "Ornaments", "Dingbats", "Webdings", "Wingdings", 59 "Fallback", "LastResort", "Al Bayan", "Al Nile", "Al Tarikh", 60 "Ayuthaya", "Baghdad", "Beirut", "Damascus", "DecoType", 61 "Diwan", "Euphemia", "Farah", "Farisi", "Geeza", "Grantha", 62 "Hiragino", "InaiMathi", "Kailasa", "Kefa", "Kohinoor", 63 "Kokonor", "Krungthep", "Mishafi", "Muna", "Nadeem", 64 "Peninim", "Plantagenet", "Raanana", "Sana", "Sathu", "Shree", 65 "Silom", "Sukhumvit", "Waseem", "Zither" 66 ] 67 68 return not any(term in font_name for term in blocked_terms) 69 70 71 def get_fonts(): 72 try: 73 result = subprocess.run( 74 ["typst", "fonts"], 75 capture_output=True, 76 text=True, 77 check=True 78 ) 79 lines = result.stdout.splitlines() 80 # Extract, clean, and immediately filter out non-Latin fonts 81 fonts = [f.strip() for f in lines if f.strip()] 82 return [f for f in fonts if is_likely_latin(f)] 83 except FileNotFoundError: 84 print("Error: 'typst' command not found.") 85 sys.exit(1) 86 87 88 def deduplicate_fonts(fonts): 89 grouped = defaultdict(list) 90 for font in fonts: 91 parts = font.split() 92 if not parts: 93 continue 94 if parts[0] in HEAVY_HITTERS: 95 prefix = parts[0] 96 else: 97 if len(parts) > 1: 98 prefix = f"{parts[0]} {parts[1]}" 99 else: 100 prefix = parts[0] 101 grouped[prefix].append(font) 102 103 final_fonts = [] 104 for prefix in sorted(grouped.keys()): 105 family = grouped[prefix] 106 if len(family) > 3: 107 mid = len(family) // 2 108 final_fonts.extend([family[0], family[mid], family[-1]]) 109 else: 110 final_fonts.extend(family) 111 return sorted(list(set(final_fonts))) 112 113 114 def generate_typst(fonts): 115 fonts_str = "\n".join(f' "{f}",' for f in fonts) 116 typst_content = f""" 117 #set page(paper: "a4", margin: (x: 1cm, y: 1cm)) 118 #set text(size: 10pt, font: "Arial") 119 120 #align(center)[ 121 #text(size: 26pt, weight: "bold")[Typst Font Specimen] \\ 122 #v(0.2em) 123 #text(size: 11pt, fill: luma(100))[Filtered & Grouped Generated: {DATE}] 124 ] 125 126 #v(1cm) 127 128 #let fonts = ( 129 {fonts_str} 130 ) 131 132 #grid( 133 columns: (1fr, 1fr), 134 column-gutter: 0.8cm, 135 row-gutter: 1.2cm, 136 ..fonts.map(f => {{ 137 block(width: 100%, breakable: false)[ 138 #text(weight: "bold", size: 9pt, fill: blue.darken(40%))[#f] \\ 139 #v(0.3em) 140 #text(font: f, size: 14pt, fallback: false)[{PANGRAM}] \\ 141 #v(0.1em) 142 #text(font: f, size: 10pt, fill: luma(80), fallback: false)[{ALPHABET}] 143 #line(length: 100%, stroke: 0.2pt + luma(220)) 144 ] 145 }}) 146 ) 147 """ 148 with open(OUTPUT_TYP, "w", encoding="utf-8") as f: 149 f.write(typst_content.strip()) 150 151 152 def main(): 153 print("Retrieving and filtering font list...") 154 fonts = get_fonts() 155 156 print("Deduplicating families...") 157 processed_fonts = deduplicate_fonts(fonts) 158 159 count = len(processed_fonts) 160 print(f"Generating Typst source ({count} readable fonts mapped)...") 161 generate_typst(processed_fonts) 162 163 print("Compiling to PDF...") 164 subprocess.run( 165 ["typst", "compile", OUTPUT_TYP, OUTPUT_PDF], 166 check=True 167 ) 168 print(f"Success! PDF generated at {OUTPUT_PDF}") 169 170 171 if __name__ == "__main__": 172 main() 173 ''; 174 in { 175 devShells.default = pkgs.mkShell { 176 name = "typst-dev-shell"; 177 178 TYPST_ROOT = "."; 179 TYPST_FONT_PATHS = "./fonts"; 180 181 packages = with pkgs; [ 182 typst 183 tinymist 184 generateFontSpecimen 185 ]; 186 }; 187 }; 188 }; 189}