Skip to content

Commit 5c29ad7

Browse files
committed
added figma import option
1 parent 5301ac4 commit 5c29ad7

File tree

5 files changed

+64
-16
lines changed

5 files changed

+64
-16
lines changed

USER.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ An exec-only endpoint for creating single week calendars is available at `/exec/
8282

8383
Select a `year` and `term` from the dropdowns and then a `week`. You do not need to change `End Week` as this will be prefilled. Then click generate to create an svg of the calender for that week. Note there is a limit of 8 events in a week with a max of 6 events in a single day. If the week exeeds this, publicity will have to be done manually.
8484

85-
If the generated SVG looks good, it can be downloaded direct to PNG via the `Save PNG` button. If frther editing is required, click `Copy SVG` to copy the SVG to your clipboard and paste it into a vector graphics editor such as Figma.
85+
If the generated SVG looks good, it can be used directly via the PNG buttons. If further editing is required, you have 2 options. Most simply, you can copy the SVG code directly using the `Copy SVG` button and paste it into your vector graphics editor of choice (such as Inkscape or Adobe Illustrator). Alternatively, you can use the `Copy Figma` button to copy a Figma compatible version of the SVG which can be pasted directly into Figma.
8686

8787
### Multi week calendar
8888

exec/publicity.py

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ def split_text(text: str, max_chars: int) -> list[str]:
363363
return lines
364364

365365

366-
def get_event_circle(event: dict, display_location: bool) -> svg.G:
366+
def get_event_circle(event: dict, display_location: bool, for_figma: bool) -> svg.G:
367367
"""Create a circle for an event"""
368368
icon = event.get("icon", "")
369369
if icon is not None:
@@ -425,15 +425,15 @@ def get_event_circle(event: dict, display_location: bool) -> svg.G:
425425

426426
lines = title + location + [time_str]
427427
current_y = text_top
428-
for line, size in zip(lines, font_sizes, strict=True):
428+
for i, (line, size) in enumerate(zip(lines, font_sizes, strict=True)):
429429
elements.append(
430430
svg.Text(
431431
text=line,
432432
x=0,
433433
y=current_y,
434434
font_size=size,
435435
text_anchor="middle",
436-
class_=["title" if size >= title_size else "text"],
436+
class_=["title" if i < len(title) or for_figma else "text"],
437437
)
438438
)
439439
current_y += size
@@ -659,7 +659,7 @@ def create_single_week( # noqa: PLR0912, PLR0915
659659

660660
# create event circle and apply offset + scaling around its center
661661
event_idx = col * row_width + row
662-
event_circle = get_event_circle(day_event_list[event_idx], True)
662+
event_circle = get_event_circle(day_event_list[event_idx], True, False)
663663
event_circle.transform = [
664664
svg.Scale(event_scale),
665665
svg.Translate(translate_x, translate_y),
@@ -669,7 +669,7 @@ def create_single_week( # noqa: PLR0912, PLR0915
669669
return elements
670670

671671

672-
def create_day_circle(events: list[dict], day_size: float) -> svg.G:
672+
def create_day_circle(events: list[dict], day_size: float, for_figma: bool) -> svg.G:
673673
"""create a circle for a day in multi week view"""
674674
# add the base background circle
675675
elements: list[svg.Element] = [svg.Circle(cx=0, cy=0, r=(day_size * 0.95) / 2, fill="#363A3E")]
@@ -701,7 +701,7 @@ def create_day_circle(events: list[dict], day_size: float) -> svg.G:
701701
# add the event circles
702702
event_circles = []
703703
for (x, y, scale), event in zip(offsets[len(events)], events, strict=True):
704-
event_circle = get_event_circle(event, False)
704+
event_circle = get_event_circle(event, False, for_figma)
705705
event_circle.transform = [
706706
svg.Scale(0.6 * scale), # overall scale to fit in day circle
707707
svg.Translate(x / scale, y / scale),
@@ -712,7 +712,9 @@ def create_day_circle(events: list[dict], day_size: float) -> svg.G:
712712
return svg.G(elements=elements)
713713

714714

715-
def create_multi_week(events: list[dict], start: Week, end: Week) -> list[svg.Element]:
715+
def create_multi_week(
716+
events: list[dict], start: Week, end: Week, for_figma: bool
717+
) -> list[svg.Element]:
716718
"""Create the calendar 5 weeks"""
717719

718720
if end.week - start.week + 1 != NUM_WEEKS:
@@ -778,7 +780,11 @@ def create_multi_week(events: list[dict], start: Week, end: Week) -> list[svg.El
778780
svg.Text(
779781
text=str(week["week"]), # str to stop week 0 not being rendered
780782
x=term_left + (WEEK_NUM_WIDTH * term_width) / 2,
781-
y=text_base + i * (week_height + TERM_WEEK_PADDING * term_height),
783+
y=(
784+
text_base
785+
+ i * (week_height + TERM_WEEK_PADDING * term_height)
786+
+ (week_height / 7.5 if for_figma else 0)
787+
),
782788
font_size=WEEK_NUM_SIZE,
783789
text_anchor="middle",
784790
dominant_baseline="middle",
@@ -793,7 +799,7 @@ def create_multi_week(events: list[dict], start: Week, end: Week) -> list[svg.El
793799

794800
# add events in each day
795801
for j, day in enumerate(all_days):
796-
day_circle = create_day_circle(day["events"], day_size)
802+
day_circle = create_day_circle(day["events"], day_size, for_figma)
797803
day_circle.transform = [
798804
svg.Translate(
799805
day_names_base + j * day_size,
@@ -813,18 +819,32 @@ def get_b64_font(path: str) -> str:
813819
return f"data:font/ttf;base64,{encoded}"
814820

815821

816-
def create_svg(start: Week, end: Week) -> str:
822+
def create_svg(start: Week, end: Week, for_figma: bool) -> str:
817823
"""Create publicity SVG calendar for events between two weeks (inclusive)"""
818824

819825
# base64 encode fonts to make svg self-contained
820826
# cairosvg only supports ttf and otf fonts
821-
montserrat_500 = get_b64_font("static/fonts/montserrat-bold.ttf")
822-
montserrat_600 = get_b64_font("static/fonts/montserrat-semibold.ttf")
827+
montserrat_500 = get_b64_font("static/fonts/montserrat-bold.ttf") if not for_figma else None
828+
montserrat_600 = get_b64_font("static/fonts/montserrat-semibold.ttf") if not for_figma else None
823829

824830
elements = [
825831
# define fonts
826832
svg.Style(
827-
text=f"""
833+
text=(
834+
"""
835+
.title {
836+
font-family: 'Montserrat';
837+
font-weight: bold;
838+
fill: white;
839+
}
840+
.text {
841+
font-family: 'Montserrat';
842+
font-weight: semibold;
843+
fill: white;
844+
}
845+
"""
846+
if for_figma
847+
else f"""
828848
@font-face {{
829849
font-family: 'montserrat-bold';
830850
src: url({montserrat_600}) format('truetype');
@@ -847,6 +867,7 @@ def create_svg(start: Week, end: Week) -> str:
847867
fill: white;
848868
}}
849869
"""
870+
)
850871
),
851872
# background
852873
svg.Rect(width=POST_WIDTH, height=POST_HEIGHT, fill=colours["grey"]),
@@ -917,6 +938,6 @@ def create_svg(start: Week, end: Week) -> str:
917938
if start == end:
918939
elements.extend(create_single_week(events, start))
919940
else:
920-
elements.extend(create_multi_week(events, start, end))
941+
elements.extend(create_multi_week(events, start, end, for_figma))
921942

922943
return str(svg.SVG(elements=elements, viewBox=svg.ViewBoxSpec(0, 0, POST_WIDTH, POST_HEIGHT)))

exec/ui.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ def publicity() -> str:
4141
return render_template("exec/publicity.html", year=year, term=term, years=get_years())
4242

4343
try:
44-
svg = create_svg(start_week, end_week)
44+
svg = create_svg(start_week, end_week, for_figma=False)
4545
except ValueError as e:
4646
flash(f"Error creating publicity SVG: {e}", "danger")
4747
return render_template("exec/publicity.html", year=year, term=term, years=get_years())
4848

49+
svg_figma = create_svg(start_week, end_week, for_figma=True)
50+
4951
return render_template(
5052
"exec/publicity.html",
5153
svg=svg,
@@ -54,4 +56,5 @@ def publicity() -> str:
5456
term=term,
5557
start_week=start,
5658
end_week=end,
59+
svg_figma=svg_figma,
5760
)

static/js/publicity.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,25 @@ document.addEventListener("DOMContentLoaded", () => {
2020
startWeekInput.dispatchEvent(new Event("input"));
2121
}
2222

23+
// copy figma button
24+
const copyFigmaButton = document.getElementById("copy-figma");
25+
copyFigmaButton.addEventListener("click", function (event) {
26+
const svgContent = document.getElementById("svg-figma").innerHTML;
27+
// copy to clipboard
28+
navigator.clipboard.writeText(svgContent).then(() => {
29+
// show success state
30+
copyFigmaButton.classList.remove("btn-outline-info");
31+
copyFigmaButton.classList.add("btn-success");
32+
copyFigmaButton.innerHTML = "<i class='ph-bold ph-check'></i> Copied";
33+
// revert after 2 seconds
34+
setTimeout(() => {
35+
copyFigmaButton.classList.remove("btn-success");
36+
copyFigmaButton.classList.add("btn-outline-info");
37+
copyFigmaButton.innerHTML = "Copy Figma";
38+
}, 2000);
39+
});
40+
});
41+
2342
// copy svg button
2443
const copyButton = document.getElementById("copy-svg");
2544
copyButton.addEventListener("click", function (event) {

templates/exec/publicity.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,18 @@ <h1><i class="ph-bold ph-instagram-logo"></i> Exec Publicity</h1>
3030
<div id="svg-container" class="w-100 h-100 d-flex align-items-center justify-content-center">
3131
{{ svg|safe }}
3232
</div>
33+
<!-- hidden svg for figma -->
34+
<div id="svg-figma" class="d-none">
35+
{{ svg_figma|safe }}
36+
</div>
3337
{% else %}
3438
<p class="text-center">Fill in the form to generate an svg</p>
3539
{% endif %}
3640
{% endwith %}
3741
</div>
3842

3943
<div class="mt-3 d-flex gap-2" id="publicity-form">
44+
<button id="copy-figma" class="btn btn-outline-info">Copy Figma</button>
4045
<button id="copy-svg" class="btn btn-outline-primary">Copy SVG</button>
4146
<button id="copy-png" class="btn btn-outline-secondary">Copy PNG</button>
4247
<button id="save-png" class="btn btn-outline-success">Save PNG</button>

0 commit comments

Comments
 (0)