Skip to content

Commit 7d20047

Browse files
authored
Merge pull request #97 from shortpoet/feature-pdf-base
fix pdf print
2 parents 8d7a6ca + 014878c commit 7d20047

File tree

6 files changed

+132
-36
lines changed

6 files changed

+132
-36
lines changed

CHANGELOG.md

Whitespace-only changes.

app/src/assets/scss/pdf/_global-pdf.scss

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,10 +218,12 @@ span.skill {
218218
color: $primary;
219219
}
220220
.skill-type {
221-
// margin: .5rem;
221+
// Adjusted for balance with body text in PDF
222+
// Previously .80rem felt heavy relative to paragraphs/headings
223+
// Keep capitalization but reduce size for better visual hierarchy
222224
color: $primary;
223225
text-transform: capitalize;
224-
font-size: .80rem;
226+
font-size: .80rem; // was .80rem
225227
}
226228
.skill-grid-render {
227229
padding: .1rem
@@ -242,10 +244,12 @@ span.skill {
242244
}
243245

244246
.skill-render {
247+
// Pill text for individual skills — tone it down slightly
248+
// Smaller text + same padding preserves readability while reducing visual weight
245249
color: $forest-green;
246250
display: inline-block;
247251
padding: 0.05rem 0.15rem 0.05rem 0.15rem;
248-
font-size: 80%;
252+
font-size: 70%; // was 80%
249253
// font-weight: 700;
250254
line-height: 1;
251255
text-align: center;
@@ -276,3 +280,74 @@ span.skill {
276280
.workingKnowledge {
277281
color: lighten($forest-green, 45%)
278282
}
283+
284+
/*
285+
* Print: stabilize header spacing in Experience entries
286+
* -----------------------------------------------------
287+
* WHAT: Normalize heading margins/line-height, prevent intra-block page breaks,
288+
* and align baselines for the row containing job title and dates.
289+
* WHY: Chrome's print UA styles + condensed display font can introduce a
290+
* visible gap between job title and company. Page-break decisions can
291+
* also leave stray space. Resetting margins, tightening line-height,
292+
* and avoiding breaks keeps the block visually tight and consistent.
293+
* HOW: Scope to @media print so screen layout remains unchanged.
294+
*/
295+
@media print {
296+
// Reset UA defaults and tame line-height for condensed headings
297+
.heading-pdf {
298+
margin: 0; // remove default heading margins that create gaps
299+
line-height: 1.1; // match condensed font metrics in print
300+
letter-spacing: normal; // ensure no unintended tracking expansion
301+
word-spacing: normal;
302+
}
303+
304+
// Ensure specific heading levels used in Experience also reset
305+
h5.heading-5,
306+
h6.heading-6 {
307+
margin: 0; // eliminate residual top/bottom margins
308+
line-height: 1.1; // keep baselines consistent across lines
309+
}
310+
311+
// Keep the three-line header (title, company, dates) together
312+
#job-position,
313+
.pdf-company,
314+
.pdf-experience-dates {
315+
break-inside: avoid; // avoid internal page breaks that leave gaps
316+
page-break-inside: avoid;
317+
}
318+
319+
.pdf-experience-dates {
320+
white-space: nowrap; // prevent wrap that can push adjacent lines down
321+
margin: 0; // guard against UA margins
322+
line-height: 1.1;
323+
}
324+
325+
.pdf-company {
326+
// a subtle top margin keeps separation without introducing a visible gap
327+
margin: 0.1rem 0 0 0;
328+
line-height: 1.1;
329+
}
330+
331+
// Align title and dates on their typographic baselines for a tighter row
332+
.d-flex.flex-row.justify-content-between {
333+
align-items: baseline !important;
334+
}
335+
}
336+
337+
/*
338+
* Print-only spacing for skill lists
339+
* ----------------------------------
340+
* WHAT: Reduce the vertical gap between skill rows without tightening text line-height.
341+
* WHY: Bootstrap's `.mb-2` adds generous spacing that feels oversized in the print layout.
342+
* HOW: Override only within print media for the two containers used in PDF skills sections.
343+
*/
344+
@media print {
345+
// Explicitly override Bootstrap margins on each row item
346+
// Reduce vertical gap between stacked skill rows without affecting column gutters
347+
.skill-list-container .skill-list.mb-2 {
348+
margin-bottom: 0.16rem !important; // was ~0.5rem; tuned for condensed print
349+
}
350+
.skill-list-container .skill-list:last-child {
351+
margin-bottom: 0 !important; // avoid trailing whitespace at the block end
352+
}
353+
}

app/src/auto-imports.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ declare global {
216216
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
217217
const useOnline: typeof import('@vueuse/core')['useOnline']
218218
const usePDFButtonInteractions: typeof import('./composables/usePDFButtonInteractions.js')['usePDFButtonInteractions']
219+
const usePDFGeneration: typeof import('./composables/usePDFGeneration.js')['usePDFGeneration']
219220
const usePDFPageSaveButton: typeof import('./composables/usePDFButtonInteractions.js')['usePDFPageSaveButton']
220221
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
221222
const useParallax: typeof import('@vueuse/core')['useParallax']
@@ -522,6 +523,7 @@ declare module 'vue' {
522523
readonly useOffsetPagination: UnwrapRef<typeof import('@vueuse/core')['useOffsetPagination']>
523524
readonly useOnline: UnwrapRef<typeof import('@vueuse/core')['useOnline']>
524525
readonly usePDFButtonInteractions: UnwrapRef<typeof import('./composables/usePDFButtonInteractions.js')['usePDFButtonInteractions']>
526+
readonly usePDFGeneration: UnwrapRef<typeof import('./composables/usePDFGeneration.js')['usePDFGeneration']>
525527
readonly usePDFPageSaveButton: UnwrapRef<typeof import('./composables/usePDFButtonInteractions.js')['usePDFPageSaveButton']>
526528
readonly usePageLeave: UnwrapRef<typeof import('@vueuse/core')['usePageLeave']>
527529
readonly useParallax: UnwrapRef<typeof import('@vueuse/core')['useParallax']>
Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,43 @@
11
<template>
22
<section class="resume-section mt-3" id="skills">
33
<div class="my-auto">
4-
<h4 style="font-family: 'Saira Extra Condensed';" class="heading-pdf heading-4 subheading mb-3 ms-0">
4+
<h4
5+
style="font-family: 'Saira Extra Condensed'"
6+
class="heading-pdf heading-4 subheading mb-3 ms-0">
57
Programming Languages &amp; Tools
68
</h4>
7-
<div id="skill-grid-container" class="d-flex flex-column justify-content-between">
9+
<div
10+
id="skill-grid-container"
11+
class="d-flex flex-column justify-content-between">
812
<div class="skill-grid-row-1 d-flex flex-row justify-content-between">
913
<div v-for="(type, it) in skills" :key="it" class="skill-grid-render">
1014
<div class="list-devicons-pdf d-flex justify-content-around">
11-
<PDFDevIcon v-for="(icon, ii) in mapIcons(type.type)" :key="ii" :source="icon.icon" :name="icon.name" />
15+
<PDFDevIcon
16+
v-for="(icon, ii) in mapIcons(type.type)"
17+
:key="ii"
18+
:source="icon.icon"
19+
:name="icon.name" />
1220
</div>
13-
<div class="skill-type-render d-flex flex-row align-items-center justify-content-around my-0">
21+
<div
22+
class="skill-type-render d-flex flex-row align-items-center justify-content-around my-0">
1423
<div>
15-
<div :class="'pdf-skill-type-' + (it + 1)" style="font-family: 'Open Sans';">{{ typeFilter(type.type) }}
24+
<div
25+
:class="'pdf-skill-type-' + (it + 1)"
26+
style="font-family: 'Open Sans'">
27+
{{ typeFilter(type.type) }}
1628
</div>
1729
</div>
1830
</div>
19-
<PDFBorder class="d-block my-2" :size=".25" />
31+
<PDFBorder class="d-block my-2" :size="0.25" />
2032
<div class="skill-list-container d-flex flex-column mb-2">
21-
<div v-for="(skill, is) in listSkills(type.details)" :key="is"
22-
class="skill-list d-flex flex-column mx-2 mb-2">
33+
<div
34+
v-for="(skill, is) in listSkills(type.details)"
35+
:key="is"
36+
class="skill-list d-flex flex-column mx-1 mb-1">
2337
<div class="skill-pill-container d-flex justify-content-around">
2438
<!-- adding extra spans to create grid -->
2539
<span></span>
26-
<span style="font-family: 'Open Sans';" class="skill-render">
40+
<span style="font-family: 'Open Sans'" class="skill-render">
2741
{{ skill }}
2842
</span>
2943
<span></span>
@@ -38,9 +52,9 @@
3852
</template>
3953

4054
<script>
41-
import PDFDevIcon from '@/components/Resume/PDF/PDFDevIcon.vue'
42-
import PDFBorder from '@/components/Resume/PDF/PDFBorder.vue'
43-
import icons from '@/assets/icons/icons.js'
55+
import PDFDevIcon from '@/components/Resume/PDF/PDFDevIcon.vue';
56+
import PDFBorder from '@/components/Resume/PDF/PDFBorder.vue';
57+
import icons from '@/assets/icons/icons.js';
4458
4559
export default {
4660
name: 'PDFSkillsRender',
@@ -59,53 +73,52 @@ export default {
5973
iconMap: icons.iconMap,
6074
windowWidth: window.innerWidth,
6175
isMediumLarge: false
62-
}
63-
},
64-
computed: {
76+
};
6577
},
78+
computed: {},
6679
methods: {
6780
listSkills(skills) {
68-
return skills.split(',')
81+
return skills.split(',');
6982
},
7083
mapIcons(skill) {
7184
let iconKeys = this.iconMap.filter(im => {
72-
console.log(im.skill, skill)
73-
return im.skill === skill
74-
})[0]['icons']
85+
console.log(im.skill, skill);
86+
return im.skill === skill;
87+
})[0]['icons'];
7588
return iconKeys.map(ik => {
76-
console.log(ik)
77-
return this.icons.filter(i => i.name === ik)[0]
78-
})
89+
console.log(ik);
90+
return this.icons.filter(i => i.name === ik)[0];
91+
});
7992
},
8093
screenCheck() {
8194
// console.log(document)
8295
// let skillGridWidth = document.getElementById('skill-grid-container').scrollWidth
83-
let windowWidth = this.windowWidth
96+
let windowWidth = this.windowWidth;
8497
// for medium-large screens
8598
if (768 < windowWidth && windowWidth < 985) {
86-
console.log('the grid should be medium-large')
87-
this.isMediumLarge = true
99+
console.log('the grid should be medium-large');
100+
this.isMediumLarge = true;
88101
} else {
89-
this.isMediumLarge = false
102+
this.isMediumLarge = false;
90103
}
91104
},
92105
typeFilter(type) {
93106
if (type === 'Document Db') {
94-
type = 'Doc Db'
107+
type = 'Doc Db';
95108
} else if (type === 'Data Vizualization') {
96-
type = 'Data Viz'
109+
type = 'Data Viz';
97110
}
98-
return type
111+
return type;
99112
}
100113
},
101114
mounted() {
102-
this.screenCheck()
115+
this.screenCheck();
103116
}
104-
}
117+
};
105118
</script>
106119
<style lang="scss">
107120
.fa-user-ninja:before {
108121
font-family: 'Font Awesome 5 Free';
109-
content: "\f504";
122+
content: '\f504';
110123
}
111124
</style>

docs/article-drafts/pdf.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# PDF Modernization
22

3-
With updates resumes to submit, the first step was to enhance the PDF generation feature of my website. This site was first built right at the start of my coding journey -- back when vibe coding involved stack overflow, github sample repos, and the debugger. The PDF generation using the canvas API seemed like magic. It rasterizes the DOM and uses another library to convert to PDF. This worked great, but I often found myself having to resize the PDF or simply copy/paste the content to adhere to updload size and/or OCR (optical character recognition) requirements.
3+
With updated resumes to submit, the first step was to enhance the PDF generation feature of my website. This site was first built right at the start of my coding journey -- back when vibe coding involved stack overflow, github sample repos, and the debugger. The PDF generation using the canvas API seemed like magic. It rasterizes the DOM and uses another library to convert to PDF. This worked great, but I often found myself having to resize the PDF or simply copy/paste the content to adhere to updload size and/or OCR (optical character recognition) requirements.
44

55
When speaking with site visitors, many didn't seem to make it to the PDF generation. Part of my desire is to streamline the resume requests, which require good discoverability. So as part of this feature, I added and additinal landing page button as well as slight animations on scroll and page load to draw the user's attention.
66

@@ -13,6 +13,7 @@ The browser print option is the quick win that allows me to ship the solution. T
1313

1414
The Cloudflare Worker option offers some distinct advantages that I am looking forward to exploring. Particularly, as it builds on some of my previous cloudflare edge working experimentation which can be found at [edge auth playground](https://ssr-dev.shortpoet.com/).
1515

16+
And, for a bit of sensationalism... Of course there was a pitfall - "Browser wars." Somehow, years later, it feels like much of tech, and by extension, our lives, are determined by the desire to control what users browse. No big surprise there. And so, of course, I had to deal with a Chrome edgecase.
1617

1718
* **PDF modernization (Phase 1):** print-optimized route, selectable text, before/after (size, time-to-first-display).
1819
Post: *“Optimization as Product Decision: when 95% smaller isn’t the whole story.”*

docs/article-drafts/thanks.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Thanks
2+
3+
I want to dedicate a thank you to the awesome colleagues at Lazlo 326 that made my experience there so rewarding. Through the cycle of challenges aand growth, it was a mutally enriching experience. I want to specially thank my teammates (and friends) Tchimwa, Sergiy, Michael, Alex, and Sagi. Thank you for your camraderie, techncial acumen, philosphical generosity, and the many laughs we shared. I will truly miss our time together.
4+
5+
I was deeply touched by the support and endorsements I received. While times of change can be daunting, the overwhelming positivity and kindness is most encouraging. Thanks again! :)

0 commit comments

Comments
 (0)