simple-resume 0.1.9__py3-none-any.whl
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.
- simple_resume/__init__.py +132 -0
- simple_resume/core/__init__.py +47 -0
- simple_resume/core/colors.py +215 -0
- simple_resume/core/config.py +672 -0
- simple_resume/core/constants/__init__.py +207 -0
- simple_resume/core/constants/colors.py +98 -0
- simple_resume/core/constants/files.py +28 -0
- simple_resume/core/constants/layout.py +58 -0
- simple_resume/core/dependencies.py +258 -0
- simple_resume/core/effects.py +154 -0
- simple_resume/core/exceptions.py +261 -0
- simple_resume/core/file_operations.py +68 -0
- simple_resume/core/generate/__init__.py +21 -0
- simple_resume/core/generate/exceptions.py +69 -0
- simple_resume/core/generate/html.py +233 -0
- simple_resume/core/generate/pdf.py +659 -0
- simple_resume/core/generate/plan.py +131 -0
- simple_resume/core/hydration.py +55 -0
- simple_resume/core/importers/__init__.py +3 -0
- simple_resume/core/importers/json_resume.py +284 -0
- simple_resume/core/latex/__init__.py +60 -0
- simple_resume/core/latex/context.py +56 -0
- simple_resume/core/latex/conversion.py +227 -0
- simple_resume/core/latex/escaping.py +68 -0
- simple_resume/core/latex/fonts.py +93 -0
- simple_resume/core/latex/formatting.py +81 -0
- simple_resume/core/latex/sections.py +218 -0
- simple_resume/core/latex/types.py +84 -0
- simple_resume/core/markdown.py +127 -0
- simple_resume/core/models.py +102 -0
- simple_resume/core/palettes/__init__.py +38 -0
- simple_resume/core/palettes/common.py +73 -0
- simple_resume/core/palettes/data/default_palettes.json +58 -0
- simple_resume/core/palettes/exceptions.py +33 -0
- simple_resume/core/palettes/fetch_types.py +52 -0
- simple_resume/core/palettes/generators.py +137 -0
- simple_resume/core/palettes/registry.py +76 -0
- simple_resume/core/palettes/resolution.py +123 -0
- simple_resume/core/palettes/sources.py +162 -0
- simple_resume/core/paths.py +21 -0
- simple_resume/core/protocols.py +134 -0
- simple_resume/core/py.typed +0 -0
- simple_resume/core/render/__init__.py +37 -0
- simple_resume/core/render/manage.py +199 -0
- simple_resume/core/render/plan.py +405 -0
- simple_resume/core/result.py +226 -0
- simple_resume/core/resume.py +609 -0
- simple_resume/core/skills.py +60 -0
- simple_resume/core/validation.py +321 -0
- simple_resume/py.typed +0 -0
- simple_resume/shell/__init__.py +3 -0
- simple_resume/shell/assets/static/css/README.md +213 -0
- simple_resume/shell/assets/static/css/common.css +641 -0
- simple_resume/shell/assets/static/css/fonts.css +42 -0
- simple_resume/shell/assets/static/css/preview.css +82 -0
- simple_resume/shell/assets/static/css/print.css +99 -0
- simple_resume/shell/assets/static/fonts/AvenirLTStd-Book.otf +0 -0
- simple_resume/shell/assets/static/fonts/AvenirLTStd-Light.otf +0 -0
- simple_resume/shell/assets/static/fonts/AvenirLTStd-Medium.otf +0 -0
- simple_resume/shell/assets/static/fonts/AvenirLTStd-Oblique.otf +0 -0
- simple_resume/shell/assets/static/fonts/AvenirLTStd-Roman.otf +0 -0
- simple_resume/shell/assets/static/fonts/fontawesome/Font Awesome 6 Brands-Regular-400.otf +0 -0
- simple_resume/shell/assets/static/fonts/fontawesome/Font Awesome 6 Free-Solid-900.otf +0 -0
- simple_resume/shell/assets/static/images/default_profile_1.jpg +0 -0
- simple_resume/shell/assets/static/images/default_profile_2.png +0 -0
- simple_resume/shell/assets/static/schema.json +236 -0
- simple_resume/shell/assets/static/themes/README.md +208 -0
- simple_resume/shell/assets/static/themes/bold.yaml +64 -0
- simple_resume/shell/assets/static/themes/classic.yaml +64 -0
- simple_resume/shell/assets/static/themes/executive.yaml +64 -0
- simple_resume/shell/assets/static/themes/minimal.yaml +64 -0
- simple_resume/shell/assets/static/themes/modern.yaml +64 -0
- simple_resume/shell/assets/templates/html/cover.html +129 -0
- simple_resume/shell/assets/templates/html/demo.html +13 -0
- simple_resume/shell/assets/templates/html/resume_base.html +453 -0
- simple_resume/shell/assets/templates/html/resume_no_bars.html +316 -0
- simple_resume/shell/assets/templates/html/resume_with_bars.html +362 -0
- simple_resume/shell/cli/__init__.py +35 -0
- simple_resume/shell/cli/main.py +975 -0
- simple_resume/shell/cli/palette.py +75 -0
- simple_resume/shell/cli/random_palette_demo.py +407 -0
- simple_resume/shell/config.py +96 -0
- simple_resume/shell/effect_executor.py +211 -0
- simple_resume/shell/file_opener.py +308 -0
- simple_resume/shell/generate/__init__.py +37 -0
- simple_resume/shell/generate/core.py +650 -0
- simple_resume/shell/generate/lazy.py +284 -0
- simple_resume/shell/io_utils.py +199 -0
- simple_resume/shell/palettes/__init__.py +1 -0
- simple_resume/shell/palettes/fetch.py +63 -0
- simple_resume/shell/palettes/loader.py +321 -0
- simple_resume/shell/palettes/remote.py +179 -0
- simple_resume/shell/pdf_executor.py +52 -0
- simple_resume/shell/py.typed +0 -0
- simple_resume/shell/render/__init__.py +1 -0
- simple_resume/shell/render/latex.py +308 -0
- simple_resume/shell/render/operations.py +240 -0
- simple_resume/shell/resume_extensions.py +737 -0
- simple_resume/shell/runtime/__init__.py +7 -0
- simple_resume/shell/runtime/content.py +190 -0
- simple_resume/shell/runtime/generate.py +497 -0
- simple_resume/shell/runtime/lazy.py +138 -0
- simple_resume/shell/runtime/lazy_import.py +173 -0
- simple_resume/shell/service_locator.py +80 -0
- simple_resume/shell/services.py +256 -0
- simple_resume/shell/session/__init__.py +6 -0
- simple_resume/shell/session/config.py +35 -0
- simple_resume/shell/session/manage.py +386 -0
- simple_resume/shell/strategies.py +181 -0
- simple_resume/shell/themes/__init__.py +35 -0
- simple_resume/shell/themes/loader.py +230 -0
- simple_resume-0.1.9.dist-info/METADATA +201 -0
- simple_resume-0.1.9.dist-info/RECORD +116 -0
- simple_resume-0.1.9.dist-info/WHEEL +4 -0
- simple_resume-0.1.9.dist-info/entry_points.txt +5 -0
- simple_resume-0.1.9.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
{%- extends "html/resume_base.html" -%}
|
|
2
|
+
|
|
3
|
+
{% block sidebar%}
|
|
4
|
+
|
|
5
|
+
<!-- Name and title moved to sidebar -->
|
|
6
|
+
<h1 style="font-size: 22pt; padding: 0; border: none; text-align: left; margin-bottom: 3mm;">{{ full_name }}</h1>
|
|
7
|
+
{% if description %}
|
|
8
|
+
<div style="font-size: 9pt; font-family: 'Avenir 35'; margin-bottom: 2mm; line-height: 1.3;">
|
|
9
|
+
{% autoescape false %}
|
|
10
|
+
{{ description }}
|
|
11
|
+
{% endautoescape %}
|
|
12
|
+
</div>
|
|
13
|
+
{% endif %}
|
|
14
|
+
|
|
15
|
+
<h3 style="padding-top: 2mm;">{{ titles["contact"] }}</h3>
|
|
16
|
+
|
|
17
|
+
{% if address %}
|
|
18
|
+
<div class="skill-container contact-block">
|
|
19
|
+
{{ contact_icon("address") }}
|
|
20
|
+
<div class="contact-container">
|
|
21
|
+
<div class="contact-text">
|
|
22
|
+
{% for x in address %}
|
|
23
|
+
<p>{{ x }}</p>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="spacer"></div>
|
|
29
|
+
{% endif %}
|
|
30
|
+
|
|
31
|
+
{% if phone %}
|
|
32
|
+
<div class="skill-container contact-block">
|
|
33
|
+
{{ contact_icon("phone") }}
|
|
34
|
+
<div class="contact-container">
|
|
35
|
+
<p class="contact-text">{{ phone }}</p>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="spacer"></div>
|
|
39
|
+
{% endif %}
|
|
40
|
+
|
|
41
|
+
{% if email %}
|
|
42
|
+
<div class="skill-container contact-block">
|
|
43
|
+
{{ contact_icon("email") }}
|
|
44
|
+
<div class="contact-container">
|
|
45
|
+
<p class="contact-text">
|
|
46
|
+
<a href="mailto:{{ email }}" style="display: block;">
|
|
47
|
+
{{ email }}
|
|
48
|
+
</a>
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="spacer"></div>
|
|
53
|
+
{% endif %}
|
|
54
|
+
|
|
55
|
+
{% if web %}
|
|
56
|
+
<div class="skill-container contact-block">
|
|
57
|
+
{{ contact_icon("web") }}
|
|
58
|
+
<div class="contact-container">
|
|
59
|
+
<p class="contact-text">
|
|
60
|
+
<a href="{{ web }}">
|
|
61
|
+
{{ web }}
|
|
62
|
+
</a>
|
|
63
|
+
</p>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="spacer"></div>
|
|
67
|
+
{% endif %}
|
|
68
|
+
|
|
69
|
+
{% if linkedin %}
|
|
70
|
+
<div class="skill-container contact-block">
|
|
71
|
+
{{ contact_icon("linkedin") }}
|
|
72
|
+
<div class="contact-container">
|
|
73
|
+
<p class="contact-text">
|
|
74
|
+
<a href="https://www.linkedin.com/{{ linkedin }}">
|
|
75
|
+
{{ linkedin }}
|
|
76
|
+
</a>
|
|
77
|
+
</p>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="spacer"></div>
|
|
81
|
+
{% endif %}
|
|
82
|
+
|
|
83
|
+
{% if github %}
|
|
84
|
+
<div class="skill-container contact-block">
|
|
85
|
+
{{ contact_icon("github") }}
|
|
86
|
+
<div class="contact-container">
|
|
87
|
+
<p class="contact-text">
|
|
88
|
+
<a href="https://github.com/{{ github }}">
|
|
89
|
+
{{ github }}
|
|
90
|
+
</a>
|
|
91
|
+
</p>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="spacer"></div>
|
|
95
|
+
{% endif %}
|
|
96
|
+
<div class="skill-spacer"></div>
|
|
97
|
+
|
|
98
|
+
<!-- Expertise -->
|
|
99
|
+
{% if expertise_groups %}
|
|
100
|
+
<h3>{{ titles["expertise"] }}</h3>
|
|
101
|
+
<div class="skill-container">
|
|
102
|
+
{% for group in expertise_groups %}
|
|
103
|
+
{% if group.title %}
|
|
104
|
+
<p style="margin-bottom: 2px;"><strong>{{ group.title }}</strong></p>
|
|
105
|
+
{% endif %}
|
|
106
|
+
{% for item in group["items"] %}
|
|
107
|
+
<p style="margin-bottom: 2px;">{{ item }}</p>
|
|
108
|
+
{% endfor %}
|
|
109
|
+
<div class="skill-spacer"></div>
|
|
110
|
+
{% endfor %}
|
|
111
|
+
</div>
|
|
112
|
+
{% endif %}
|
|
113
|
+
|
|
114
|
+
{% if certification_groups %}
|
|
115
|
+
<h3>{{ titles["certification"] }}</h3>
|
|
116
|
+
<div class="skill-container">
|
|
117
|
+
{% for group in certification_groups %}
|
|
118
|
+
{% if group.title %}
|
|
119
|
+
<p style="margin-bottom: 2px;"><strong>{{ group.title }}</strong></p>
|
|
120
|
+
{% endif %}
|
|
121
|
+
{% set available_width = resume_config.get('sidebar_width', 65) - resume_config.get('sidebar_padding_left', 10) - resume_config.get('sidebar_padding_right', 10) %}
|
|
122
|
+
{{ render_sidebar_entries(group["items"], available_width, 9, 7) }}
|
|
123
|
+
<div class="skill-spacer"></div>
|
|
124
|
+
{% endfor %}
|
|
125
|
+
</div>
|
|
126
|
+
{% endif %}
|
|
127
|
+
|
|
128
|
+
{% if keyskills_groups %}
|
|
129
|
+
<h3>{{ titles["keyskills"] }}</h3>
|
|
130
|
+
<div class="skill-container">
|
|
131
|
+
{% for group in keyskills_groups %}
|
|
132
|
+
{% if group.title %}
|
|
133
|
+
<p style="margin-bottom: var(--keyskills-item-margin-bottom, 0.5mm);"><strong>{{ group.title }}</strong></p>
|
|
134
|
+
{% endif %}
|
|
135
|
+
{% for item in group["items"] %}
|
|
136
|
+
<p class="keyskills-item">
|
|
137
|
+
<span class="keyskills-bullet" style="color: {{ resume_config.get('sidebar_text_color', '#000000') }};">•</span>
|
|
138
|
+
{{ item }}
|
|
139
|
+
</p>
|
|
140
|
+
{% endfor %}
|
|
141
|
+
<div class="skill-spacer"></div>
|
|
142
|
+
{% endfor %}
|
|
143
|
+
</div>
|
|
144
|
+
{% endif %}
|
|
145
|
+
|
|
146
|
+
{% endblock %}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
{% block body%}
|
|
150
|
+
{% for name, block_data in body.items() %}
|
|
151
|
+
<h2 class="section-heading">
|
|
152
|
+
<span class="section-heading-marker">
|
|
153
|
+
<span class="section-heading-icon">
|
|
154
|
+
{{ section_icon_inline(name) }}
|
|
155
|
+
</span>
|
|
156
|
+
</span>
|
|
157
|
+
<span class="section-heading-text">{{ name }}</span>
|
|
158
|
+
</h2>
|
|
159
|
+
|
|
160
|
+
{% for data in block_data %}
|
|
161
|
+
<div class="content">
|
|
162
|
+
|
|
163
|
+
<!-- Date Sidebar -->
|
|
164
|
+
<div class="date-container">
|
|
165
|
+
<p class="date1">
|
|
166
|
+
{{ data["end"] }}
|
|
167
|
+
</p>
|
|
168
|
+
|
|
169
|
+
{% if data["start"] %}
|
|
170
|
+
<p class="date-separator" style="text-align: center; font-size: 8pt; padding-top: 1mm; color: {{ resume_config.get('date2_color', '#616161') }};">—</p>
|
|
171
|
+
<p class="date2">
|
|
172
|
+
{{ data["start"] }}
|
|
173
|
+
</p>
|
|
174
|
+
{% endif %}
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<!-- Content itself -->
|
|
178
|
+
<div class="description-container">
|
|
179
|
+
|
|
180
|
+
<!-- Calculate font size based on combined length of title and company -->
|
|
181
|
+
{% set heading_width_mm = resume_config.get("page_width", 210) - resume_config.get("sidebar_width", 65) - resume_config.get("padding", 12) - resume_config.get("date_container_width", 15) - 3 - resume_config.get("description_container_padding_left", 4) - 1 %}
|
|
182
|
+
{% set title_text = data["title"] | default("", true) %}
|
|
183
|
+
{% set company_text = data["company"] | default("", true) %}
|
|
184
|
+
|
|
185
|
+
<!-- Calculate combined font size to prevent overlapping and wrapping -->
|
|
186
|
+
{% set combined_text = title_text ~ " " ~ company_text %}
|
|
187
|
+
{% set combined_text_length = (title_text | length) + (company_text | length) + 1 %}
|
|
188
|
+
|
|
189
|
+
<!-- Tiered reduction based on combined text length -->
|
|
190
|
+
{% if combined_text_length > 75 %}
|
|
191
|
+
{# Very long (75+): aggressive reduction #}
|
|
192
|
+
{% set combined_font_size = dynamic_font_size(combined_text, heading_width_mm * 0.65, 11.5, 7.0) or "11.5pt" %}
|
|
193
|
+
{% elif combined_text_length > 55 %}
|
|
194
|
+
{# Long (55-75): moderate reduction #}
|
|
195
|
+
{% set combined_font_size = dynamic_font_size(combined_text, heading_width_mm * 0.72, 11.5, 7.5) or "11.5pt" %}
|
|
196
|
+
{% elif combined_text_length > 45 %}
|
|
197
|
+
{# Medium (45-55): light reduction #}
|
|
198
|
+
{% set combined_font_size = dynamic_font_size(combined_text, heading_width_mm * 0.80, 11.5, 8.5) or "11.5pt" %}
|
|
199
|
+
{% else %}
|
|
200
|
+
{# Short: standard sizing #}
|
|
201
|
+
{% set combined_font_size = dynamic_font_size(combined_text, heading_width_mm * 0.90, 11.5, 9.0) or "11.5pt" %}
|
|
202
|
+
{% endif %}
|
|
203
|
+
{% set title_font_size = combined_font_size %}
|
|
204
|
+
{% set company_font_size = combined_font_size %}
|
|
205
|
+
{% set title_font_numeric = (title_font_size | replace("pt", "") | float) %}
|
|
206
|
+
{% set title_line_height = "1.2" %}
|
|
207
|
+
{% set margin_bottom = "2px" %}
|
|
208
|
+
{% if title_font_numeric <= 9 %}
|
|
209
|
+
{% set title_line_height = "1.1" %}
|
|
210
|
+
{% set margin_bottom = "0px" %}
|
|
211
|
+
{% endif %}
|
|
212
|
+
|
|
213
|
+
<!-- Smart color selection based on luminance analysis -->
|
|
214
|
+
{% set theme_color = resume_config.get("theme_color", "#0395DE") %}
|
|
215
|
+
{% set icon_accent_color = resume_config.get("heading_icon_color", theme_color) %}
|
|
216
|
+
{% set company_color = theme_color %}
|
|
217
|
+
{% set company_link_color = theme_color %}
|
|
218
|
+
|
|
219
|
+
{% if theme_color.startswith('#') %}
|
|
220
|
+
{% set hex_color = theme_color[1:] %}
|
|
221
|
+
{% if hex_color|length == 3 %}
|
|
222
|
+
{% set r = (hex_color[0:1] * 2) | int(base=16) %}
|
|
223
|
+
{% set g = (hex_color[1:2] * 2) | int(base=16) %}
|
|
224
|
+
{% set b = (hex_color[2:3] * 2) | int(base=16) %}
|
|
225
|
+
{% elif hex_color|length == 6 %}
|
|
226
|
+
{% set r = hex_color[0:2] | int(base=16) %}
|
|
227
|
+
{% set g = hex_color[2:4] | int(base=16) %}
|
|
228
|
+
{% set b = hex_color[4:6] | int(base=16) %}
|
|
229
|
+
{% else %}
|
|
230
|
+
{% set r = 0 %}
|
|
231
|
+
{% set g = 0 %}
|
|
232
|
+
{% set b = 0 %}
|
|
233
|
+
{% endif %}
|
|
234
|
+
|
|
235
|
+
{% set luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 %}
|
|
236
|
+
{% if luminance >= 0.5 %}
|
|
237
|
+
{% set company_color = icon_accent_color %}
|
|
238
|
+
{% set company_link_color = icon_accent_color %}
|
|
239
|
+
{% endif %}
|
|
240
|
+
{% else %}
|
|
241
|
+
{# Fallback for non-hex colors #}
|
|
242
|
+
{% set company_color = icon_accent_color %}
|
|
243
|
+
{% set company_link_color = icon_accent_color %}
|
|
244
|
+
{% endif %}
|
|
245
|
+
|
|
246
|
+
<div class="heading-row">
|
|
247
|
+
<div class="heading-title">
|
|
248
|
+
<!-- If there is description use h4, else regular text -->
|
|
249
|
+
{% if data["description"] %}<h4 style="font-size: {{ title_font_size }} !important; line-height: {{ title_line_height }} !important; margin-bottom: {{ margin_bottom }} !important; word-wrap: break-word; overflow-wrap: break-word; font-family: 'Avenir 45' !important; font-weight: normal !important; color: var(--text-color, #000000) !important;">{% else %}<p style="font-size: {{ title_font_size }} !important; line-height: {{ title_line_height }} !important; margin-bottom: {{ margin_bottom }} !important; word-wrap: break-word; overflow-wrap: break-word; font-family: 'Avenir 45' !important; font-weight: normal !important; color: var(--text-color, #000000) !important;">{% endif %}
|
|
250
|
+
|
|
251
|
+
<!-- Allows to add a link to the title -->
|
|
252
|
+
{% if data["title_link"] %}<a href="{{ data['title_link'] }}" style="font-size: {{ title_font_size }} !important; word-wrap: break-word; overflow-wrap: break-word; font-family: 'Avenir 45' !important; font-weight: normal !important; color: var(--text-color, #000000) !important;">{% endif %}
|
|
253
|
+
{{ data["title"] }}
|
|
254
|
+
{% if data["title_link"] %}</a>{% endif %}
|
|
255
|
+
|
|
256
|
+
{% if data["description"] %}</h4>{% else %}</p>{% endif %}
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
{% if company_text %}
|
|
260
|
+
<div class="heading-company">
|
|
261
|
+
<h5 style="font-size: {{ company_font_size }} !important; line-height: {{ title_line_height }} !important; font-family: 'Avenir 45' !important; font-weight: normal !important; color: {{ company_color }} !important; margin: 0px !important;">
|
|
262
|
+
<!-- Allows to add a link to the company -->
|
|
263
|
+
{% if data["company_link"] %}<a href="{{ data['company_link'] }}" style="font-size: {{ company_font_size }} !important; font-family: 'Avenir 45' !important; font-weight: normal !important; color: {{ company_link_color }} !important;">{% endif %}
|
|
264
|
+
{{ data["company"] }}
|
|
265
|
+
{% if data["company_link"] %}</a>{% endif %}
|
|
266
|
+
</h5>
|
|
267
|
+
</div>
|
|
268
|
+
{% endif %}
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
{% if data["description"] %}
|
|
272
|
+
<div class="description">
|
|
273
|
+
{% autoescape false %}
|
|
274
|
+
{{ data["description"] }}
|
|
275
|
+
{% endautoescape %}
|
|
276
|
+
</div>
|
|
277
|
+
{% endif %}
|
|
278
|
+
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
{% if data.get("tech_stack") %}
|
|
282
|
+
<!-- Tech Stack Row -->
|
|
283
|
+
<div class="date-container" style="padding-top: 3mm;">
|
|
284
|
+
<p style="font-size: 8pt; font-family: 'Avenir 45'; color: {{ resume_config.get('date2_color', '#616161') }}; text-align: center; width: {{ resume_config.get("date_container_width", 15) }}mm;">Tech Stack</p>
|
|
285
|
+
</div>
|
|
286
|
+
<div class="description-container" style="padding-top: 3mm;">
|
|
287
|
+
<p style="font-size: 8pt; font-family: 'Avenir 45'; line-height: 1.4; margin: 0;">{{ data["tech_stack"] }}</p>
|
|
288
|
+
</div>
|
|
289
|
+
{% endif %}
|
|
290
|
+
|
|
291
|
+
{% set activities = data.get("activities") %}
|
|
292
|
+
{% if name | lower == "education" and activities %}
|
|
293
|
+
<!-- Activities Row -->
|
|
294
|
+
<div class="date-container" style="padding-top: 3mm;">
|
|
295
|
+
<p style="font-size: 8pt; font-family: 'Avenir 45'; color: {{ resume_config.get('date2_color', '#616161') }}; text-align: center; width: {{ resume_config.get("date_container_width", 15) }}mm;">Activities</p>
|
|
296
|
+
</div>
|
|
297
|
+
<div class="description-container" style="padding-top: 3mm;">
|
|
298
|
+
<p style="font-size: 8pt; font-family: 'Avenir 45'; line-height: 1.4; margin: 0;">
|
|
299
|
+
{% if activities is string %}
|
|
300
|
+
{{ activities }}
|
|
301
|
+
{% else %}
|
|
302
|
+
{{ activities | join(", ") }}
|
|
303
|
+
{% endif %}
|
|
304
|
+
</p>
|
|
305
|
+
</div>
|
|
306
|
+
{% endif %}
|
|
307
|
+
|
|
308
|
+
</div>
|
|
309
|
+
|
|
310
|
+
{% if not loop.last %}<div class="entry-spacer"></div>{% endif %}
|
|
311
|
+
{% endfor %}
|
|
312
|
+
|
|
313
|
+
<div class="spacer"></div>
|
|
314
|
+
{% endfor %}
|
|
315
|
+
|
|
316
|
+
{% endblock %}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
{% extends "html/resume_base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block sidebar%}
|
|
4
|
+
|
|
5
|
+
<!-- Name and title moved to sidebar -->
|
|
6
|
+
<h1 style="font-size: 22pt; padding: 0; border: none; text-align: left; margin-bottom: 3mm;">{{ full_name }}</h1>
|
|
7
|
+
{% if description %}
|
|
8
|
+
<div style="font-size: 9pt; font-family: 'Avenir 35'; margin-bottom: 2mm; line-height: 1.3;">
|
|
9
|
+
{% autoescape false %}
|
|
10
|
+
{{ description }}
|
|
11
|
+
{% endautoescape %}
|
|
12
|
+
</div>
|
|
13
|
+
{% endif %}
|
|
14
|
+
|
|
15
|
+
<h3 style="padding-top: 2mm;">{{ titles["contact"] }}</h3>
|
|
16
|
+
|
|
17
|
+
{% if address %}
|
|
18
|
+
<div class="skill-container contact-block">
|
|
19
|
+
{{ contact_icon("address") }}
|
|
20
|
+
<div class="contact-container">
|
|
21
|
+
<div class="contact-text"> <!-- Allow multiline in address -->
|
|
22
|
+
{% for x in address %}
|
|
23
|
+
<p>{{ x }}</p>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="spacer"></div>
|
|
29
|
+
{% endif %}
|
|
30
|
+
|
|
31
|
+
{% if phone %}
|
|
32
|
+
<div class="skill-container contact-block">
|
|
33
|
+
{{ contact_icon("phone") }}
|
|
34
|
+
<div class="contact-container">
|
|
35
|
+
<p class="contact-text">{{ phone }}</p>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="spacer"></div>
|
|
39
|
+
{% endif %}
|
|
40
|
+
|
|
41
|
+
{% if email %}
|
|
42
|
+
<div class="skill-container contact-block">
|
|
43
|
+
{{ contact_icon("email") }}
|
|
44
|
+
<div class="contact-container">
|
|
45
|
+
<p class="contact-text">
|
|
46
|
+
<a href="mailto:{{ email }}" style="display: block;">
|
|
47
|
+
{{ email }}
|
|
48
|
+
</a>
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="spacer"></div>
|
|
53
|
+
{% endif %}
|
|
54
|
+
|
|
55
|
+
{% if web %}
|
|
56
|
+
<div class="skill-container contact-block">
|
|
57
|
+
{{ contact_icon("web") }}
|
|
58
|
+
<div class="contact-container">
|
|
59
|
+
<p class="contact-text">
|
|
60
|
+
<a href="{{ web }}">
|
|
61
|
+
{{ web }}
|
|
62
|
+
</a>
|
|
63
|
+
</p>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="spacer"></div>
|
|
67
|
+
{% endif %}
|
|
68
|
+
|
|
69
|
+
{% if linkedin %}
|
|
70
|
+
<div class="skill-container contact-block">
|
|
71
|
+
{{ contact_icon("linkedin") }}
|
|
72
|
+
<div class="contact-container">
|
|
73
|
+
<p class="contact-text">
|
|
74
|
+
<a href="https://www.linkedin.com/{{ linkedin }}">
|
|
75
|
+
{{ linkedin }}
|
|
76
|
+
</a>
|
|
77
|
+
</p>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="spacer"></div>
|
|
81
|
+
{% endif %}
|
|
82
|
+
|
|
83
|
+
{% if github %}
|
|
84
|
+
<div class="skill-container contact-block">
|
|
85
|
+
{{ contact_icon("github") }}
|
|
86
|
+
<div class="contact-container">
|
|
87
|
+
<p class="contact-text">
|
|
88
|
+
<a href="https://github.com/{{ github }}">
|
|
89
|
+
{{ github }}
|
|
90
|
+
</a>
|
|
91
|
+
</p>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="spacer"></div>
|
|
95
|
+
{% endif %}
|
|
96
|
+
<div class="skill-spacer"></div>
|
|
97
|
+
|
|
98
|
+
<!-- Skills Section with Horizontal Bar Charts -->
|
|
99
|
+
{% if skills %}
|
|
100
|
+
<h3>{{ titles["skills"] or "Skills" }}</h3>
|
|
101
|
+
<div class="skill-container">
|
|
102
|
+
{% for name, value in skills.items() %}
|
|
103
|
+
{% set normalized_value = (value / 100.0) * (resume_config.get('sidebar_width', 65) - 2*resume_config.get('padding', 12)) %}
|
|
104
|
+
<p style="margin-bottom: 0;">
|
|
105
|
+
{{ name }}
|
|
106
|
+
</p>
|
|
107
|
+
<div class="bar-container">
|
|
108
|
+
<div class="bar-blue" style="width: {{ normalized_value }}mm"></div>
|
|
109
|
+
<div class="bar-grey" style="width: {{ resume_config.get('sidebar_width', 65) - 2*resume_config.get('padding', 12) - normalized_value }}mm"></div>
|
|
110
|
+
</div>
|
|
111
|
+
<div class="skill-spacer"></div>
|
|
112
|
+
{% endfor %}
|
|
113
|
+
</div>
|
|
114
|
+
{% endif %}
|
|
115
|
+
|
|
116
|
+
<!-- Certification -->
|
|
117
|
+
{% if certification %}
|
|
118
|
+
<h3>{{ titles["certification"] }}</h3>
|
|
119
|
+
<div class="skill-container">
|
|
120
|
+
{% set available_width = resume_config.get('sidebar_width', 65) - resume_config.get('sidebar_padding_left', 10) - resume_config.get('sidebar_padding_right', 10) %}
|
|
121
|
+
{{ render_sidebar_entries(certification, available_width, 9, 7) }}
|
|
122
|
+
</div>
|
|
123
|
+
{% endif %}
|
|
124
|
+
|
|
125
|
+
<!-- Languages Section -->
|
|
126
|
+
{% if languages %}
|
|
127
|
+
<h3>{{ titles["languages"] or "Languages" }}</h3>
|
|
128
|
+
<div class="skill-container">
|
|
129
|
+
{% for name, value in languages.items() %}
|
|
130
|
+
{% set normalized_value = (value / 100.0) * (resume_config.get('sidebar_width', 65) - 2*resume_config.get('padding', 12)) %}
|
|
131
|
+
<p style="margin-bottom: 0;">
|
|
132
|
+
{{ name }}
|
|
133
|
+
</p>
|
|
134
|
+
<div class="bar-container">
|
|
135
|
+
<div class="bar-blue" style="width: {{ normalized_value }}mm"></div>
|
|
136
|
+
<div class="bar-grey" style="width: {{ resume_config.get('sidebar_width', 65) - 2*resume_config.get('padding', 12) - normalized_value }}mm"></div>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="skill-spacer"></div>
|
|
139
|
+
{% endfor %}
|
|
140
|
+
</div>
|
|
141
|
+
{% endif %}
|
|
142
|
+
|
|
143
|
+
<!-- Expertise -->
|
|
144
|
+
{% if expertise_groups %}
|
|
145
|
+
<h3>{{ titles["expertise"] }}</h3>
|
|
146
|
+
<div class="skill-container">
|
|
147
|
+
{% for group in expertise_groups %}
|
|
148
|
+
{% if group.title %}
|
|
149
|
+
<h4 style="margin: 2px 0; font-size: 9pt; font-weight: 600; color: {{ resume_config.get('sidebar_text_color', '#000000') }};">{{ group.title }}</h4>
|
|
150
|
+
{% endif %}
|
|
151
|
+
{% for item in group["items"] %}
|
|
152
|
+
<p style="margin-bottom: 2px; margin-left: 0.5mm; position: relative; padding-left: 2mm;">
|
|
153
|
+
<span style="position: absolute; left: 0; top: 0; color: {{ resume_config.get('sidebar_text_color', '#000000') }}; opacity: 0.6;">•</span>
|
|
154
|
+
{{ item }}
|
|
155
|
+
</p>
|
|
156
|
+
{% endfor %}
|
|
157
|
+
<div class="skill-spacer"></div>
|
|
158
|
+
{% endfor %}
|
|
159
|
+
</div>
|
|
160
|
+
{% endif %}
|
|
161
|
+
|
|
162
|
+
<!-- Programming -->
|
|
163
|
+
{% if programming_groups %}
|
|
164
|
+
<h3>{{ titles["programming"] }}</h3>
|
|
165
|
+
<div class="skill-container">
|
|
166
|
+
{% for group in programming_groups %}
|
|
167
|
+
{% if group.title %}
|
|
168
|
+
<h4 style="margin: 2px 0; font-size: 9pt; font-weight: 600; color: {{ resume_config.get('sidebar_text_color', '#000000') }};">{{ group.title }}</h4>
|
|
169
|
+
{% endif %}
|
|
170
|
+
{% set available_width = resume_config.get('sidebar_width', 65) - resume_config.get('sidebar_padding_left', 10) - resume_config.get('sidebar_padding_right', 10) %}
|
|
171
|
+
{{ render_sidebar_entries(group["items"], available_width, 9, 7) }}
|
|
172
|
+
<div class="skill-spacer"></div>
|
|
173
|
+
{% endfor %}
|
|
174
|
+
</div>
|
|
175
|
+
{% endif %}
|
|
176
|
+
|
|
177
|
+
<!-- Key Skills -->
|
|
178
|
+
{% if keyskills_groups %}
|
|
179
|
+
<h3>{{ titles["keyskills"] }}</h3>
|
|
180
|
+
<div class="skill-container">
|
|
181
|
+
{% for group in keyskills_groups %}
|
|
182
|
+
{% if group.title %}
|
|
183
|
+
<h4 style="margin: 2px 0; font-size: 9pt; font-weight: 600; color: {{ resume_config.get('sidebar_text_color', '#000000') }};">{{ group.title }}</h4>
|
|
184
|
+
{% endif %}
|
|
185
|
+
{% for item in group["items"] %}
|
|
186
|
+
<p class="keyskills-item">
|
|
187
|
+
<span class="keyskills-bullet" style="color: {{ resume_config.get('sidebar_text_color', '#000000') }};">•</span>
|
|
188
|
+
{{ item }}
|
|
189
|
+
</p>
|
|
190
|
+
{% endfor %}
|
|
191
|
+
<div class="skill-spacer"></div>
|
|
192
|
+
{% endfor %}
|
|
193
|
+
</div>
|
|
194
|
+
{% endif %}
|
|
195
|
+
|
|
196
|
+
{% endblock %}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
{% block body%}
|
|
200
|
+
{% for name, block_data in body.items() %}
|
|
201
|
+
<h2 class="section-heading">
|
|
202
|
+
<span class="section-heading-marker">
|
|
203
|
+
<span class="section-heading-icon">
|
|
204
|
+
{{ section_icon_inline(name) }}
|
|
205
|
+
</span>
|
|
206
|
+
</span>
|
|
207
|
+
<span class="section-heading-text">{{ name }}</span>
|
|
208
|
+
</h2>
|
|
209
|
+
|
|
210
|
+
{% for data in block_data %}
|
|
211
|
+
<div class="content">
|
|
212
|
+
|
|
213
|
+
<!-- Date Sidebar -->
|
|
214
|
+
<div class="date-container">
|
|
215
|
+
<p class="date1">
|
|
216
|
+
{{ data["end"] }}
|
|
217
|
+
</p>
|
|
218
|
+
|
|
219
|
+
{% if data["start"] %}
|
|
220
|
+
<p class="date-separator" style="text-align: center; font-size: 8pt; padding-top: 1mm; color: {{ resume_config.get('date2_color', '#616161') }};">—</p>
|
|
221
|
+
<p class="date2">
|
|
222
|
+
{{ data["start"] }}
|
|
223
|
+
</p>
|
|
224
|
+
{% endif %}
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<!-- Content itself -->
|
|
228
|
+
<div class="description-container">
|
|
229
|
+
|
|
230
|
+
<!-- Calculate font size based on combined length of title and company -->
|
|
231
|
+
{% set heading_width_mm = resume_config.get("page_width", 210) - resume_config.get("sidebar_width", 65) - resume_config.get("padding", 12) - resume_config.get("date_container_width", 15) - 3 - resume_config.get("description_container_padding_left", 4) - 1 %}
|
|
232
|
+
{% set title_text = data["title"] | default("", true) %}
|
|
233
|
+
{% set company_text = data["company"] | default("", true) %}
|
|
234
|
+
|
|
235
|
+
<!-- Calculate combined font size to prevent overlapping and wrapping -->
|
|
236
|
+
{% set combined_text = title_text ~ " " ~ company_text %}
|
|
237
|
+
{% set combined_text_length = (title_text | length) + (company_text | length) + 1 %}
|
|
238
|
+
|
|
239
|
+
<!-- Tiered reduction based on combined text length -->
|
|
240
|
+
{% if combined_text_length > 75 %}
|
|
241
|
+
{# Very long (75+): aggressive reduction #}
|
|
242
|
+
{% set combined_font_size = dynamic_font_size(combined_text, heading_width_mm * 0.65, 11.5, 7.0) or "11.5pt" %}
|
|
243
|
+
{% elif combined_text_length > 55 %}
|
|
244
|
+
{# Long (55-75): moderate reduction #}
|
|
245
|
+
{% set combined_font_size = dynamic_font_size(combined_text, heading_width_mm * 0.72, 11.5, 7.5) or "11.5pt" %}
|
|
246
|
+
{% elif combined_text_length > 45 %}
|
|
247
|
+
{# Medium (45-55): light reduction #}
|
|
248
|
+
{% set combined_font_size = dynamic_font_size(combined_text, heading_width_mm * 0.80, 11.5, 8.5) or "11.5pt" %}
|
|
249
|
+
{% else %}
|
|
250
|
+
{# Short: standard sizing #}
|
|
251
|
+
{% set combined_font_size = dynamic_font_size(combined_text, heading_width_mm * 0.90, 11.5, 9.0) or "11.5pt" %}
|
|
252
|
+
{% endif %}
|
|
253
|
+
{% set title_font_size = combined_font_size %}
|
|
254
|
+
{% set company_font_size = combined_font_size %}
|
|
255
|
+
{% set title_font_numeric = (title_font_size | replace("pt", "") | float) %}
|
|
256
|
+
{% set title_line_height = "1.2" %}
|
|
257
|
+
{% set margin_bottom = "2px" %}
|
|
258
|
+
{% if title_font_numeric <= 9 %}
|
|
259
|
+
{% set title_line_height = "1.1" %}
|
|
260
|
+
{% set margin_bottom = "0px" %}
|
|
261
|
+
{% endif %}
|
|
262
|
+
|
|
263
|
+
<!-- Smart color selection based on luminance analysis -->
|
|
264
|
+
{% set theme_color = resume_config.get("theme_color", "#0395DE") %}
|
|
265
|
+
{% set company_color = theme_color %}
|
|
266
|
+
{% set company_link_color = theme_color %}
|
|
267
|
+
|
|
268
|
+
{% if theme_color.startswith('#') %}
|
|
269
|
+
{% set hex_color = theme_color[1:] %}
|
|
270
|
+
{% if hex_color|length == 3 %}
|
|
271
|
+
{% set r = (hex_color[0:1] * 2) | int(base=16) %}
|
|
272
|
+
{% set g = (hex_color[1:2] * 2) | int(base=16) %}
|
|
273
|
+
{% set b = (hex_color[2:3] * 2) | int(base=16) %}
|
|
274
|
+
{% elif hex_color|length == 6 %}
|
|
275
|
+
{% set r = hex_color[0:2] | int(base=16) %}
|
|
276
|
+
{% set g = hex_color[2:4] | int(base=16) %}
|
|
277
|
+
{% set b = hex_color[4:6] | int(base=16) %}
|
|
278
|
+
{% else %}
|
|
279
|
+
{% set r = 0 %}
|
|
280
|
+
{% set g = 0 %}
|
|
281
|
+
{% set b = 0 %}
|
|
282
|
+
{% endif %}
|
|
283
|
+
|
|
284
|
+
{% set luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255 %}
|
|
285
|
+
{% if luminance >= 0.5 %}
|
|
286
|
+
{% set company_color = "#2c3e50" %}
|
|
287
|
+
{% set company_link_color = "#2c3e50" %}
|
|
288
|
+
{% endif %}
|
|
289
|
+
{% else %}
|
|
290
|
+
{# Fallback for non-hex colors #}
|
|
291
|
+
{% set company_color = "#2c3e50" %}
|
|
292
|
+
{% set company_link_color = "#2c3e50" %}
|
|
293
|
+
{% endif %}
|
|
294
|
+
|
|
295
|
+
<div class="heading-row">
|
|
296
|
+
<div class="heading-title">
|
|
297
|
+
<!-- If there is description use h4, else regular text -->
|
|
298
|
+
{% if data["description"] %}<h4 style="font-size: {{ title_font_size }} !important; line-height: {{ title_line_height }} !important; margin-bottom: {{ margin_bottom }} !important; word-wrap: break-word; overflow-wrap: break-word; font-family: 'Avenir 45' !important; font-weight: normal !important; color: var(--text-color, #000000) !important;">{% else %}<p style="font-size: {{ title_font_size }} !important; line-height: {{ title_line_height }} !important; margin-bottom: {{ margin_bottom }} !important; word-wrap: break-word; overflow-wrap: break-word; font-family: 'Avenir 45' !important; font-weight: normal !important; color: var(--text-color, #000000) !important;">{% endif %}
|
|
299
|
+
|
|
300
|
+
<!-- Allows to add a link to the title -->
|
|
301
|
+
{% if data["title_link"] %}<a href="{{ data['title_link'] }}" style="font-size: {{ title_font_size }} !important; word-wrap: break-word; overflow-wrap: break-word; font-family: 'Avenir 45' !important; font-weight: normal !important; color: var(--text-color, #000000) !important;">{% endif %}
|
|
302
|
+
{{ data["title"] }}
|
|
303
|
+
{% if data["title_link"] %}</a>{% endif %}
|
|
304
|
+
|
|
305
|
+
{% if data["description"] %}</h4>{% else %}</p>{% endif %}
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
{% if company_text %}
|
|
309
|
+
<div class="heading-company">
|
|
310
|
+
<h5 style="font-size: {{ company_font_size }} !important; line-height: {{ title_line_height }} !important; font-family: 'Avenir 45' !important; font-weight: normal !important; color: {{ company_color }} !important; margin: 0px !important;">
|
|
311
|
+
<!-- Allows to add a link to the company -->
|
|
312
|
+
{% if data["company_link"] %}<a href="{{ data['company_link'] }}" style="font-size: {{ company_font_size }} !important; font-family: 'Avenir 45' !important; font-weight: normal !important; color: {{ company_link_color }} !important;">{% endif %}
|
|
313
|
+
{{ data["company"] }}
|
|
314
|
+
{% if data["company_link"] %}</a>{% endif %}
|
|
315
|
+
</h5>
|
|
316
|
+
</div>
|
|
317
|
+
{% endif %}
|
|
318
|
+
</div>
|
|
319
|
+
|
|
320
|
+
{% if data["description"] %}
|
|
321
|
+
<div class="description">
|
|
322
|
+
{% autoescape false %}
|
|
323
|
+
{{ data["description"] }}
|
|
324
|
+
{% endautoescape %}
|
|
325
|
+
</div>
|
|
326
|
+
{% endif %}
|
|
327
|
+
|
|
328
|
+
</div>
|
|
329
|
+
|
|
330
|
+
{% if data.get("tech_stack") %}
|
|
331
|
+
<div class="date-container tech-stack-label-container">
|
|
332
|
+
<p class="tech-stack-label">Tech Stack</p>
|
|
333
|
+
</div>
|
|
334
|
+
<div class="description-container tech-stack-value-container">
|
|
335
|
+
<p class="tech-stack-value">{{ data["tech_stack"] }}</p>
|
|
336
|
+
</div>
|
|
337
|
+
{% endif %}
|
|
338
|
+
|
|
339
|
+
{% set activities = data.get("activities") %}
|
|
340
|
+
{% if name | lower == "education" and activities %}
|
|
341
|
+
<!-- Activities Row -->
|
|
342
|
+
<div class="date-container" style="padding-top: 3mm;">
|
|
343
|
+
<p style="font-size: 8pt; font-family: 'Avenir 45'; color: {{ resume_config.get('date2_color', '#616161') }}; text-align: center; width: {{ resume_config.get("padding", 12) }}mm;">Activities</p>
|
|
344
|
+
</div>
|
|
345
|
+
<div class="description-container" style="padding-top: 3mm;">
|
|
346
|
+
<p style="font-size: 8pt; font-family: 'Avenir 45'; line-height: 1.4; margin: 0;">
|
|
347
|
+
{% if activities is string %}
|
|
348
|
+
{{ activities }}
|
|
349
|
+
{% else %}
|
|
350
|
+
{{ activities | join(", ") }}
|
|
351
|
+
{% endif %}
|
|
352
|
+
</p>
|
|
353
|
+
</div>
|
|
354
|
+
{% endif %}
|
|
355
|
+
|
|
356
|
+
</div>
|
|
357
|
+
{% endfor %}
|
|
358
|
+
|
|
359
|
+
<div class="spacer"></div>
|
|
360
|
+
{% endfor %}
|
|
361
|
+
|
|
362
|
+
{% endblock %}
|