from fontTools.ttLib import TTFont import os def check_font_integrity(font_path): print(f"\n{'='*60}") print(f"Checking: {os.path.basename(font_path)}") print(f"{'='*60}") try: font = TTFont(font_path) # Basic info size = os.path.getsize(font_path) print(f"File size: {size/1024/1024:.1f} MB") print(f"Glyph count: {len(font.getGlyphOrder())}") # Check required tables for TrueType required_tables = ['cmap', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post'] missing_tables = [t for t in required_tables if t not in font.keys()] if missing_tables: print(f"❌ MISSING TABLES: {missing_tables}") return False print(f"✅ All required tables present") # Check glyf table (glyph outlines) if 'glyf' in font: glyf = font['glyf'] glyph_names = list(glyf.glyphs.keys()) if hasattr(glyf, 'glyphs') else [] if not glyph_names and hasattr(glyf, 'keys'): glyph_names = list(glyf.keys()) empty_glyphs = 0 valid_glyphs = 0 for name in font.getGlyphOrder()[:100]: # Check first 100 glyphs try: if name in ['.notdef']: continue glyph_set = font.getGlyphSet() if name in glyph_set: from fontTools.pens.boundsPen import BoundsPen pen = BoundsPen(glyph_set) glyph_set[name].draw(pen) if pen.bounds is None: empty_glyphs += 1 else: valid_glyphs += 1 except Exception as e: pass print(f"Glyph check (first 100): {valid_glyphs} valid, {empty_glyphs} empty") if valid_glyphs == 0 and empty_glyphs > 10: print("⚠️ WARNING: Most glyphs appear to be empty!") return False # Check cmap (character mapping) if 'cmap' in font: cmap = font['cmap'] cmap_tables = cmap.tables chinese_chars_found = 0 test_chars = ['香', '菇', '甲', '鱼', '家', '常', '菜'] for table in cmap_tables: if hasattr(table, 'cmap'): for char in test_chars: code = ord(char) if code in table.cmap: chinese_chars_found += 1 print(f"CJK chars in cmap: {chinese_chars_found}/{len(test_chars)}") if chinese_chars_found == 0: print("❌ CRITICAL: No CJK characters found in cmap!") return False # Try to get a specific CJK glyph test_char = '香' glyph_set = font.getGlyphSet() if test_char in glyph_set: print(f"✅ Glyph '{test_char}' accessible") # Get bounds to verify it has actual shape data from fontTools.pens.boundsPen import BoundsPen pen = BoundsPen(glyph_set) try: glyph_set[test_char].draw(pen) if pen.bounds: xMin, yMin, xMax, yMax = pen.bounds width = xMax - xMin height = yMax - yMin print(f" Bounds: ({xMin}, {yMin}) - ({xMax}, {yMax})") print(f" Size: {width} x {height} units") if width < 10 or height < 10: print(" ⚠️ Glyph appears too small (possibly corrupted)") return False else: print(" ⚠️ No bounds (empty glyph?)") return False except Exception as e: print(f" ❌ Error drawing glyph: {e}") return False else: print(f"❌ Glyph '{test_char}' NOT accessible") return False font.close() return True except Exception as e: print(f"❌ Error opening font: {e}") import traceback traceback.print_exc() return False # Check both OTF (original) and TTF (converted) versions fonts_to_check = [ ('assets/fonts/NotoSansSC-Regular.otf', 'Original OTF'), ('assets/fonts/NotoSansSC-Regular.ttf', 'Converted TTF'), ('assets/fonts/NotoSansSC-Bold.otf', 'Bold Original OTF'), ('assets/fonts/NotoSansSC-Bold.ttf', 'Bold Converted TTF'), ] results = {} for path, label in fonts_to_check: if os.path.exists(path): results[label] = check_font_integrity(path) else: print(f"\n⚠️ File not found: {path}") print("\n" + "="*60) print("SUMMARY") print("="*60) for label, passed in results.items(): status = "✅ PASS" if passed else "❌ FAIL" print(f"{label}: {status}") all_passed = all(results.values()) if all_passed: print("\n🎉 All fonts are valid!") else: print("\n⚠️ Some fonts have issues - this explains the PDF rendering problem")