codeannex 0.4.3__tar.gz → 0.4.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {codeannex-0.4.3 → codeannex-0.4.4}/PKG-INFO +6 -2
  2. {codeannex-0.4.3 → codeannex-0.4.4}/README.md +5 -1
  3. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/__main__.py +7 -0
  4. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/interface/cli.py +30 -9
  5. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/renderer/fonts.py +7 -1
  6. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/renderer/text_utils.py +29 -14
  7. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex.egg-info/PKG-INFO +6 -2
  8. {codeannex-0.4.3 → codeannex-0.4.4}/pyproject.toml +1 -1
  9. {codeannex-0.4.3 → codeannex-0.4.4}/LICENSE +0 -0
  10. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/__init__.py +0 -0
  11. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/core/__init__.py +0 -0
  12. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/core/config.py +0 -0
  13. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/core/pdf_builder.py +0 -0
  14. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/interface/__init__.py +0 -0
  15. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/io/__init__.py +0 -0
  16. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/io/file_utils.py +0 -0
  17. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/io/git_utils.py +0 -0
  18. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/renderer/__init__.py +0 -0
  19. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex/renderer/highlight.py +0 -0
  20. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex.egg-info/SOURCES.txt +0 -0
  21. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex.egg-info/dependency_links.txt +0 -0
  22. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex.egg-info/entry_points.txt +0 -0
  23. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex.egg-info/requires.txt +0 -0
  24. {codeannex-0.4.3 → codeannex-0.4.4}/codeannex.egg-info/top_level.txt +0 -0
  25. {codeannex-0.4.3 → codeannex-0.4.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeannex
3
- Version: 0.4.3
3
+ Version: 0.4.4
4
4
  Summary: Generates a professional PDF source code annex with Smart Index, Images and Emoji support.
5
5
  License: MIT
6
6
  Project-URL: Repository, https://github.com/tanhleno/codeannex
@@ -36,6 +36,8 @@ Generates a professional PDF annex from a project's source code — featuring sy
36
36
  ## 🚀 Key Features
37
37
 
38
38
  - **Interactive Wizard 2.0** — Step-by-step configuration with smart sections (Project, Style, Typography, Layout, Filters) and explicit default prompts.
39
+ - **Smart Emoji Support** — Automatic discovery of standard emoji fonts (**Segoe UI Emoji** on Windows, **DejaVu Sans** on Linux).
40
+ - **Graceful Emoji Fallback** — If no emoji font is found, it uses official Unicode names (e.g., `[ROCKET]`) for perfect readability.
39
41
  - **Git Version Tracking** — Automatically detects **Repository URL**, **Branch**, and **Commit SHA**. Smart root detection avoids Git metadata on subdirectories.
40
42
  - **Smart SVG Rendering** — Files are rendered as both a high-quality image and XML code. Entries are intelligently deduplicated in the summary.
41
43
  - **Improved Document Structure** — Subdirectories and their contents are listed before root files for better organization.
@@ -58,7 +60,7 @@ For full SVG support (required for crisp line numbers and SVG image rendering):
58
60
  pipx install "codeannex[svg]"
59
61
  ```
60
62
 
61
- *Alternatively, you can use standard pip:* `pip install codeannex`
63
+ *Note: For best emoji rendering on Linux, you may want to install `fonts-noto-color-emoji` or `ttf-dejavu`.*
62
64
 
63
65
  ## 📖 Usage
64
66
 
@@ -102,6 +104,8 @@ Default output filename is `{project_name}_code_annex.pdf`.
102
104
  ### Fonts
103
105
  - `--font-path PATH` — Additional directory to search for `.ttf`/`.otf` files.
104
106
  - `--title-font` / `--normal-font` / `--mono-font` — System font names.
107
+ - `--emoji-font NAME` — Custom font for emojis.
108
+ - `--emoji-description` — Force textual descriptions (e.g., `[GRINNING FACE]`) instead of glyphs.
105
109
 
106
110
  ## 🧪 Testing
107
111
 
@@ -5,6 +5,8 @@ Generates a professional PDF annex from a project's source code — featuring sy
5
5
  ## 🚀 Key Features
6
6
 
7
7
  - **Interactive Wizard 2.0** — Step-by-step configuration with smart sections (Project, Style, Typography, Layout, Filters) and explicit default prompts.
8
+ - **Smart Emoji Support** — Automatic discovery of standard emoji fonts (**Segoe UI Emoji** on Windows, **DejaVu Sans** on Linux).
9
+ - **Graceful Emoji Fallback** — If no emoji font is found, it uses official Unicode names (e.g., `[ROCKET]`) for perfect readability.
8
10
  - **Git Version Tracking** — Automatically detects **Repository URL**, **Branch**, and **Commit SHA**. Smart root detection avoids Git metadata on subdirectories.
9
11
  - **Smart SVG Rendering** — Files are rendered as both a high-quality image and XML code. Entries are intelligently deduplicated in the summary.
10
12
  - **Improved Document Structure** — Subdirectories and their contents are listed before root files for better organization.
@@ -27,7 +29,7 @@ For full SVG support (required for crisp line numbers and SVG image rendering):
27
29
  pipx install "codeannex[svg]"
28
30
  ```
29
31
 
30
- *Alternatively, you can use standard pip:* `pip install codeannex`
32
+ *Note: For best emoji rendering on Linux, you may want to install `fonts-noto-color-emoji` or `ttf-dejavu`.*
31
33
 
32
34
  ## 📖 Usage
33
35
 
@@ -71,6 +73,8 @@ Default output filename is `{project_name}_code_annex.pdf`.
71
73
  ### Fonts
72
74
  - `--font-path PATH` — Additional directory to search for `.ttf`/`.otf` files.
73
75
  - `--title-font` / `--normal-font` / `--mono-font` — System font names.
76
+ - `--emoji-font NAME` — Custom font for emojis.
77
+ - `--emoji-description` — Force textual descriptions (e.g., `[GRINNING FACE]`) instead of glyphs.
74
78
 
75
79
  ## 🧪 Testing
76
80
 
@@ -36,6 +36,13 @@ def check_emoji_font_style():
36
36
 
37
37
 
38
38
  def main():
39
+ try:
40
+ _main_impl()
41
+ except KeyboardInterrupt:
42
+ print(f"\n\n\033[33m⚠️ Operation aborted by user (Ctrl+C).\033[0m")
43
+ sys.exit(0)
44
+
45
+ def _main_impl():
39
46
  args, unknown = parse_args()
40
47
  is_interactive = len(sys.argv) <= 2 and not args.no_input
41
48
  if is_interactive: run_interactive_wizard(args)
@@ -2,6 +2,7 @@ import argparse
2
2
  from pathlib import Path
3
3
  from ..core.config import PDFConfig
4
4
  from ..io.git_utils import get_git_info, get_git_remotes
5
+ from ..renderer.fonts import register_emoji_font
5
6
 
6
7
  # ANSI Colors for a better CLI experience
7
8
  BOLD = "\033[1m"
@@ -96,19 +97,27 @@ def run_interactive_wizard(args):
96
97
  if has_git:
97
98
  if len(remotes) > 1:
98
99
  _print_header(2, total_steps, "Repository Info", "Multiple Git remotes detected")
99
- print(f" Available remotes:")
100
100
  remote_names = list(remotes.keys())
101
+
102
+ # Identify default index
103
+ default_idx = 1
104
+ if "origin" in remote_names:
105
+ default_idx = remote_names.index("origin") + 1
106
+
101
107
  for i, name in enumerate(remote_names, 1):
102
- print(f" {i}. {name} ({remotes[name]})")
103
-
104
- choice = _input_field("Select remote (number) or press Enter for origin", "1")
105
- if choice.isdigit() and 1 <= int(choice) <= len(remote_names):
108
+ marker = f"{GREEN}*{RESET}" if i == default_idx else " "
109
+ print(f" {i}. {marker} {name} ({remotes[name]})")
110
+ print(f" 0. [ Manual / None ]")
111
+
112
+ choice = _input_field(f"Select remote (1-{len(remote_names)}) or '0' for manual", str(default_idx))
113
+
114
+ if choice == "0":
115
+ git_url = None
116
+ elif choice.isdigit() and 1 <= int(choice) <= len(remote_names):
106
117
  selected_remote = remote_names[int(choice)-1]
107
118
  git_url = remotes[selected_remote]
108
- elif "origin" in remotes:
109
- git_url = remotes["origin"]
110
119
  else:
111
- git_url = remotes[remote_names[0]]
120
+ git_url = remotes[remote_names[default_idx-1]]
112
121
  elif len(remotes) == 1:
113
122
  git_url = list(remotes.values())[0]
114
123
 
@@ -137,11 +146,23 @@ def run_interactive_wizard(args):
137
146
  args.normal_font = _input_field("Normal Font", args.normal_font or 'Helvetica') or args.normal_font
138
147
  args.mono_font = _input_field("Monospace Font", args.mono_font or 'Auto') or args.mono_font
139
148
  args.code_size = int(_input_field("Code Font Size", args.code_size) or args.code_size)
140
-
149
+
141
150
  paths = _input_field("Additional Font Paths (comma-separated)", "None")
142
151
  if paths:
143
152
  args.font_path = [p.strip() for p in paths.split(",") if p.strip()]
144
153
 
154
+ # Emoji Font Check & Support
155
+ emoji_f, emoji_p = register_emoji_font()
156
+ if not emoji_f:
157
+ print(f"\n {YELLOW}⚠️ No emoji font detected!{RESET}")
158
+ print(f" To render emojis, install {BOLD}Symbola{RESET} or {BOLD}Google Noto Emoji{RESET}.")
159
+ print(f" Or provide a custom path above.")
160
+
161
+ choice = input(f" Use text descriptions for emojis (e.g. [smile])? (y/{GREEN}N{RESET}): ").strip().lower()
162
+ if choice == 'y':
163
+ args.emoji_description = True
164
+ else:
165
+ print(f" {GREEN}✅ Emoji font detected:{RESET} {emoji_f} ({emoji_p or 'System'})")
145
166
  # 5. Page Layout
146
167
  if _ask_section(5, total_steps, "Page Layout & Margins", "Margins, Paper Size, Page Numbering"):
147
168
  args.margin_top = float(_input_field("Top Margin (cm)", args.margin_top or 2.0) or (args.margin_top or 2.0))
@@ -86,7 +86,13 @@ TTF_SEARCH_PATHS = [
86
86
  "/usr/share/fonts/truetype/noto/NotoSansMono-Regular.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", "/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf",
87
87
  "C:\\Windows\\Fonts\\consola.ttf", "C:\\Windows\\Fonts\\cour.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf", "C:\\Windows\\Fonts\\arial.ttf"
88
88
  ]
89
- EMOJI_SEARCH_PATHS = ["/usr/share/fonts/truetype/noto/NotoEmoji-Regular.ttf", "/usr/share/fonts/truetype/ancient-scripts/Symbola_hint.ttf"]
89
+ EMOJI_SEARCH_PATHS = [
90
+ "C:\\Windows\\Fonts\\seguiemj.ttf", # Windows Standard Emoji
91
+ "C:\\Windows\\Fonts\\seguisym.ttf", # Windows Standard Symbol
92
+ "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", # Linux Standard Fallback
93
+ "/usr/share/fonts/truetype/noto/NotoEmoji-Regular.ttf",
94
+ "/usr/share/fonts/truetype/ancient-scripts/Symbola_hint.ttf"
95
+ ]
90
96
 
91
97
  def _register_font(name: str, paths: list, fallback):
92
98
  for p in paths:
@@ -1,4 +1,5 @@
1
1
  import re
2
+ import unicodedata
2
3
  from reportlab.lib import colors
3
4
  from reportlab.pdfbase import pdfmetrics
4
5
 
@@ -7,16 +8,29 @@ def sanitize_text(text: str) -> str:
7
8
  if not text: return ""
8
9
  return re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f]', '', text)
9
10
 
11
+ def _get_emoji_label(char: str) -> str:
12
+ """Returns a textual label for an emoji character."""
13
+ try:
14
+ name = unicodedata.name(char)
15
+ return f"[{name}]"
16
+ except (ValueError, KeyError):
17
+ return f"[Emoji-{ord(char):X}]"
18
+
10
19
  def get_safe_string_width(text, font_name, font_size, emoji_font=None, emoji_description=False):
11
20
  from .fonts import is_char_supported
12
21
  total_w = 0.0
13
22
  for char in text:
14
23
  if is_char_supported(char, font_name):
15
24
  total_w += pdfmetrics.stringWidth(char, font_name, font_size)
25
+ elif emoji_description:
26
+ label = _get_emoji_label(char)
27
+ total_w += pdfmetrics.stringWidth(label, font_name, font_size)
16
28
  elif emoji_font:
17
- if emoji_description: total_w += pdfmetrics.stringWidth(f"[{char}]", font_name, font_size)
18
- else: total_w += pdfmetrics.stringWidth(char, emoji_font, font_size)
19
- else: total_w += pdfmetrics.stringWidth("?", font_name, font_size)
29
+ total_w += pdfmetrics.stringWidth(char, emoji_font, font_size)
30
+ else:
31
+ # Fallback to description if no font is available, even if emoji_description=False
32
+ label = _get_emoji_label(char)
33
+ total_w += pdfmetrics.stringWidth(label, font_name, font_size)
20
34
  return total_w
21
35
 
22
36
  def draw_text_with_fallback(canvas, x, y, text, font_name, font_size, emoji_font=None, color=None, emoji_description=False):
@@ -28,20 +42,21 @@ def draw_text_with_fallback(canvas, x, y, text, font_name, font_size, emoji_font
28
42
  canvas.setFont(font_name, font_size)
29
43
  canvas.drawString(curr_x, y, char)
30
44
  curr_x += pdfmetrics.stringWidth(char, font_name, font_size)
45
+ elif emoji_description:
46
+ canvas.setFont(font_name, font_size)
47
+ label = _get_emoji_label(char)
48
+ canvas.drawString(curr_x, y, label)
49
+ curr_x += pdfmetrics.stringWidth(label, font_name, font_size)
31
50
  elif emoji_font:
32
- if emoji_description:
33
- canvas.setFont(font_name, font_size)
34
- label = f"[{char}]"
35
- canvas.drawString(curr_x, y, label)
36
- curr_x += pdfmetrics.stringWidth(label, font_name, font_size)
37
- else:
38
- canvas.setFont(emoji_font, font_size)
39
- canvas.drawString(curr_x, y, char)
40
- curr_x += pdfmetrics.stringWidth(char, emoji_font, font_size)
51
+ canvas.setFont(emoji_font, font_size)
52
+ canvas.drawString(curr_x, y, char)
53
+ curr_x += pdfmetrics.stringWidth(char, emoji_font, font_size)
41
54
  else:
55
+ # Fallback to description if no font is available
42
56
  canvas.setFont(font_name, font_size)
43
- canvas.drawString(curr_x, y, "?")
44
- curr_x += pdfmetrics.stringWidth("?", font_name, font_size)
57
+ label = _get_emoji_label(char)
58
+ canvas.drawString(curr_x, y, label)
59
+ curr_x += pdfmetrics.stringWidth(label, font_name, font_size)
45
60
  return curr_x
46
61
 
47
62
  def draw_centred_text_with_fallback(canvas, x, y, text, font_name, font_size, emoji_font=None, color=None, emoji_description=False):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codeannex
3
- Version: 0.4.3
3
+ Version: 0.4.4
4
4
  Summary: Generates a professional PDF source code annex with Smart Index, Images and Emoji support.
5
5
  License: MIT
6
6
  Project-URL: Repository, https://github.com/tanhleno/codeannex
@@ -36,6 +36,8 @@ Generates a professional PDF annex from a project's source code — featuring sy
36
36
  ## 🚀 Key Features
37
37
 
38
38
  - **Interactive Wizard 2.0** — Step-by-step configuration with smart sections (Project, Style, Typography, Layout, Filters) and explicit default prompts.
39
+ - **Smart Emoji Support** — Automatic discovery of standard emoji fonts (**Segoe UI Emoji** on Windows, **DejaVu Sans** on Linux).
40
+ - **Graceful Emoji Fallback** — If no emoji font is found, it uses official Unicode names (e.g., `[ROCKET]`) for perfect readability.
39
41
  - **Git Version Tracking** — Automatically detects **Repository URL**, **Branch**, and **Commit SHA**. Smart root detection avoids Git metadata on subdirectories.
40
42
  - **Smart SVG Rendering** — Files are rendered as both a high-quality image and XML code. Entries are intelligently deduplicated in the summary.
41
43
  - **Improved Document Structure** — Subdirectories and their contents are listed before root files for better organization.
@@ -58,7 +60,7 @@ For full SVG support (required for crisp line numbers and SVG image rendering):
58
60
  pipx install "codeannex[svg]"
59
61
  ```
60
62
 
61
- *Alternatively, you can use standard pip:* `pip install codeannex`
63
+ *Note: For best emoji rendering on Linux, you may want to install `fonts-noto-color-emoji` or `ttf-dejavu`.*
62
64
 
63
65
  ## 📖 Usage
64
66
 
@@ -102,6 +104,8 @@ Default output filename is `{project_name}_code_annex.pdf`.
102
104
  ### Fonts
103
105
  - `--font-path PATH` — Additional directory to search for `.ttf`/`.otf` files.
104
106
  - `--title-font` / `--normal-font` / `--mono-font` — System font names.
107
+ - `--emoji-font NAME` — Custom font for emojis.
108
+ - `--emoji-description` — Force textual descriptions (e.g., `[GRINNING FACE]`) instead of glyphs.
105
109
 
106
110
  ## 🧪 Testing
107
111
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codeannex"
7
- version = "0.4.3"
7
+ version = "0.4.4"
8
8
  description = "Generates a professional PDF source code annex with Smart Index, Images and Emoji support."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
File without changes
File without changes