File size: 6,000 Bytes
774ed1c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import argparse
import markdown
import yaml
from weasyprint import HTML
from pathlib import Path
from typing import Dict, Tuple, Any

class ResumeConverter:
    def __init__(self, input_path: Path, template_path: Path):
        self.input_path = input_path
        self.template_path = template_path
        self.markdown_content = self._read_file(input_path)
        self.template_content = self._read_file(template_path)

    @staticmethod
    def _read_file(filepath: Path) -> str:
        """Read content from a file."""
        with open(filepath, 'r', encoding='utf-8') as f:
            return f.read()

    def _parse_markdown(self) -> Tuple[Dict[str, Any], str]:
        """Parse markdown content with YAML frontmatter."""
        # Split content into lines
        lines = self.markdown_content.splitlines()

        # Get name from first line
        name = lines[0].lstrip('# ').strip()

        # Find YAML content
        yaml_lines = []
        content_lines = []
        in_yaml = False

        for line in lines[1:]:
            if line.strip() == 'header:' or line.strip() == 'social:':
                in_yaml = True
                yaml_lines.append(line)
            elif in_yaml:
                if line and (line.startswith('  ') or line.startswith('\t')):
                    yaml_lines.append(line)
                else:
                    in_yaml = False
                    content_lines.append(line)
            else:
                content_lines.append(line)

        # Parse YAML
        yaml_content = '\n'.join(yaml_lines)
        try:
            metadata = yaml.safe_load(yaml_content)
        except yaml.YAMLError as e:
            print(f"Error parsing YAML: {e}")
            metadata = {}

        metadata['name'] = name
        content = '\n'.join(content_lines)

        return metadata, content

    def _generate_icon(self, icon: str) -> str:
        """Generate icon HTML from either Font Awesome class or emoji."""
        if not icon:
            return ''
        # If icon starts with 'fa' or contains 'fa-', treat as Font Awesome
        if icon.startswith('fa') or 'fa-' in icon:
            return f'<i class="{icon}"></i>'
        # Otherwise, treat as emoji
        return f'<span class="emoji">{icon}</span>'

    def _generate_social_links_html(self, social_data: Dict[str, Dict[str, str]]) -> str:
        """Generate HTML for social links section."""
        social_items = []

        for platform, data in social_data.items():
            icon = data['icon']
            # For Font Awesome icons, add fa-brands class to enable brand colors
            if icon.startswith('fa') or 'fa-' in icon:
                icon = f"fa-brands {icon}" if 'fa-brands' not in icon else icon
            icon_html = self._generate_icon(icon)
            item = f'''<a href="{data['url']}" class="social-link" target="_blank">
                {icon_html}
                <span>{data['text']}</span>
            </a>'''
            social_items.append(item)

        return '\n'.join(social_items)

    def convert_to_html(self) -> str:
        """Convert markdown to HTML using template."""
        # Parse markdown and YAML
        metadata, content = self._parse_markdown()

        # Convert markdown content
        html_content = markdown.markdown(content, extensions=['extra'])

        # Generate social links section
        if 'social' in metadata:
            social_html = self._generate_social_links_html(metadata['social'])
        else:
            social_html = ''

        # Replace template placeholders
        html = self.template_content.replace('{{name}}', metadata['name'])
        html = html.replace('{{title}}', f"{metadata['name']}'s Resume")
        html = html.replace('{{content}}', html_content)
        html = html.replace('<!-- SOCIAL_LINKS -->', social_html)

        # Replace header information
        if 'header' in metadata:
            header = metadata['header']
            html = html.replace('{{header_title}}', header.get('title', ''))
            html = html.replace('{{header_email}}', header.get('email', ''))
            html = html.replace('{{header_phone}}', header.get('phone', ''))
            html = html.replace('{{header_location}}', header.get('location', ''))

        return html

    def save_html(self, output_path: Path, html_content: str) -> None:
        """Save HTML content to file."""
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(html_content)
        print(f"Created HTML file: {output_path}")

    def save_pdf(self, output_path: Path, html_content: str) -> None:
        """Convert HTML to PDF and save."""
        try:
            HTML(string=html_content).write_pdf(output_path)
            print(f"Created PDF file: {output_path}")
        except Exception as e:
            print(f"Error converting to PDF: {e}")

def main():
    parser = argparse.ArgumentParser(description='Convert markdown resume to HTML/PDF')
    parser.add_argument('input', nargs='?', default='resume.md',
                      help='Input markdown file (default: resume.md)')
    parser.add_argument('--template', default='template.html',
                      help='HTML template file (default: template.html)')
    parser.add_argument('--output-html', help='Output HTML file')
    parser.add_argument('--output-pdf', help='Output PDF file')
    args = parser.parse_args()

    # Process paths
    input_path = Path(args.input)
    template_path = Path(args.template)
    output_html = Path(args.output_html) if args.output_html else input_path.with_suffix('.html')
    output_pdf = Path(args.output_pdf) if args.output_pdf else input_path.with_suffix('.pdf')

    # Create converter and process files
    converter = ResumeConverter(input_path, template_path)
    html_content = converter.convert_to_html()

    # Save output files
    converter.save_html(output_html, html_content)
    converter.save_pdf(output_pdf, html_content)

if __name__ == '__main__':
    main()