my-last-cv 0.0.4 → 0.0.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf2509574951b9a1befe15309696db3b3dbfd5ce8196fafbd8ec48d2d4ae9d80
4
- data.tar.gz: f0c6d142419598ce2246156320db87a98062667acd39e8ed30fbc06529745366
3
+ metadata.gz: 5d2bc19ff6ce026645ff9fc68e65345b76a3087db661be1eac66a48379b3395e
4
+ data.tar.gz: 9edb1b606e386db51aa0272ea2ed87fe290c6273619fe5b65213430aa117663e
5
5
  SHA512:
6
- metadata.gz: 32586b06068dcc7b74e3700581634f6bf693108b87181641edf25c69084e1ad42258fd64fee58492a8fd509bf0fc1873a3d4efb5291504383fd508a2857d625a
7
- data.tar.gz: 7ce669851940b2e5f9c1fdec663eb8d84f90b75da8ef3e927c111d58bd038c55727386e0742b6135dc184c69e09c9f06f9ad5366f9422b3329f1eab9b40c6808
6
+ metadata.gz: e9f25db116d350985c4a016bfea94876b926702c606e93faf3e8daf239e1b027f89654fddc0d3495cc10746a1c723f98057b848058e2a32dbefe02a6e13ee8d6
7
+ data.tar.gz: f2802143d81a0d3a0a2fb9e8bd46f550bf093db531e9e43172d1d0746d24713251a90bdeed255034e6593e951c2c64993f5214ae14c0bb7456b27b6d14c73582
data/README.md CHANGED
@@ -7,3 +7,8 @@ bundle add my-last-cv
7
7
  bundle install
8
8
  bundle exec my_last_cv sample/cv.md output/cv.pdf
9
9
  ```
10
+
11
+ ## Custom fonts
12
+ By default, the gem looks for fonts in `./fonts` of the calling project.
13
+ You can override this with `Style.new(fonts_dir: "/path/to/fonts")`
14
+ or `MY_LAST_CV_FONTS_DIR=/path/to/fonts`.
@@ -5,29 +5,112 @@ module MyLastCV
5
5
  end
6
6
 
7
7
  def parse
8
- lignes = @markdown.lines.map(&:chomp)
9
- resultat = { sections: [] }
10
- section_actuelle = nil
11
-
12
- lignes.each do |ligne|
13
- next if ligne.strip.empty?
14
- if (m = ligne.match(/^#\s+(.*)/))
15
- resultat[:name] = m[1].strip
16
- elsif (m = ligne.match(/^(email|phone|location):\s*(.+)/i))
17
- resultat[:contact] ||= []
18
- resultat[:contact] << m[2].strip
19
- elsif (m = ligne.match(/^##\s+(.*)/))
20
- section_actuelle = { title: m[1].strip, items: [] }
21
- resultat[:sections] << section_actuelle
22
- elsif (m = ligne.match(/^[-*]\s+(.*)/))
23
- section_actuelle ||= { title: "Divers", items: [] }
24
- resultat[:sections] << section_actuelle unless resultat[:sections].include?(section_actuelle)
25
- section_actuelle[:items] << m[1].strip
8
+ lines = @markdown.lines.map(&:chomp)
9
+ result = { sections: [] }
10
+
11
+ current_section = nil
12
+ current_element = nil
13
+
14
+ # buffer
15
+ pending = []
16
+
17
+ # helpers
18
+ flush_pending_to = lambda do |target|
19
+ return if pending.empty?
20
+ text = pending.join(" ").strip
21
+ pending.clear
22
+ return if text.empty?
23
+ target[:items] ||= []
24
+ target[:items] << { type: :paragraph, text: text }
25
+ end
26
+
27
+ flush_pending_nowhere = lambda do
28
+ return if pending.empty?
29
+ text = pending.join(" ").strip
30
+ pending.clear
31
+ return if text.empty?
32
+ result[:intro] ||= []
33
+ result[:intro] << text
34
+ end
35
+
36
+ lines.each do |raw|
37
+ line = raw.rstrip
38
+ next if line.strip.empty?
39
+
40
+ if (m = line.match(/^#\s+(.*)/)) # New header
41
+ if current_element
42
+ flush_pending_to.call(current_element)
43
+ elsif current_section
44
+ flush_pending_to.call(current_section)
45
+ else
46
+ flush_pending_nowhere.call
47
+ end
48
+
49
+ result[:name] = m[1].strip
50
+ current_section = nil
51
+ current_element = nil
52
+
53
+ elsif (m = line.match(/^(email|phone|location):\s*(.+)/i))
54
+ (result[:contact] ||= []) << m[2].strip
55
+
56
+ elsif (m = line.match(/^##\s+(.*)/)) # New section
57
+ if current_element
58
+ flush_pending_to.call(current_element)
59
+ elsif current_section
60
+ flush_pending_to.call(current_section)
61
+ else
62
+ flush_pending_nowhere.call
63
+ end
64
+
65
+ current_section = { title: m[1].strip, items: [], elements: [] }
66
+ result[:sections] << current_section
67
+ current_element = nil
68
+
69
+ elsif (m = line.match(/^###\s+(.*)/)) # New element
70
+ if current_element
71
+ flush_pending_to.call(current_element)
72
+ elsif current_section
73
+ flush_pending_to.call(current_section)
74
+ else
75
+ flush_pending_nowhere.call
76
+ end
77
+
78
+ current_section ||= { title: "Divers", items: [], elements: [] }
79
+ result[:sections] << current_section unless result[:sections].include?(current_section)
80
+
81
+ current_element = { title: m[1].strip, items: [] }
82
+ current_section[:elements] ||= []
83
+ current_section[:elements] << current_element
84
+
85
+ elsif (m = line.match(/^[-*]\s+(.*)/)) # Bullet point
86
+ item = { type: :bullet, text: m[1].strip }
87
+ if current_element
88
+ flush_pending_to.call(current_element)
89
+ current_element[:items] << item
90
+ elsif current_section
91
+ flush_pending_to.call(current_section)
92
+ current_section[:items] << item
93
+ else
94
+ current_section = { title: "Divers", items: [item], elements: [] }
95
+ result[:sections] << current_section
96
+ end
97
+
98
+ else
99
+ pending << line.strip
26
100
  end
27
101
  end
28
102
 
29
- resultat[:contact] = (resultat[:contact] || []).join(" · ")
30
- resultat
103
+ # End of parsing, flush any remaining pending text
104
+ if current_element
105
+ flush_pending_to.call(current_element)
106
+ elsif current_section
107
+ flush_pending_to.call(current_section)
108
+ else
109
+ flush_pending_nowhere.call
110
+ end
111
+
112
+ result[:contact] = (result[:contact] || []).join(" · ")
113
+ result
31
114
  end
32
115
  end
33
116
  end
@@ -1,5 +1,5 @@
1
1
  require 'prawn'
2
- require "fileutils"
2
+ require 'fileutils'
3
3
 
4
4
  module MyLastCV
5
5
  class Renderer
@@ -57,6 +57,17 @@ module MyLastCV
57
57
  pdf.move_down 6
58
58
  pdf.font(@style.body_font)
59
59
  pdf.text(@parsed_cv[:contact] || '', size: @style.body_size, align: :center)
60
+
61
+ if @parsed_cv[:intro]&.any?
62
+ pdf.move_down 10
63
+ @parsed_cv[:intro].each do |p|
64
+ pdf.text(p, size: @style.body_size, leading: 2)
65
+ pdf.move_down 4
66
+ end
67
+ else
68
+ pdf.move_down 4
69
+ end
70
+
60
71
  with_color(pdf, @style.accent_color) do
61
72
  pdf.stroke_horizontal_rule
62
73
  end
@@ -64,14 +75,38 @@ module MyLastCV
64
75
  end
65
76
 
66
77
  def render_sections(pdf)
67
- @parsed_cv[:sections].each do |section|
78
+ (@parsed_cv[:sections] || []).each do |section|
68
79
  pdf.move_down 8
69
80
  pdf.font(@style.section_font)
70
81
  pdf.text(section[:title], size: @style.section_size, style: :bold)
71
- pdf.move_down 4
72
- pdf.font(@style.body_font)
73
- section[:items].each do |item|
74
- pdf.text("• #{item}", size: @style.body_size, indent_paragraphs: 16)
82
+
83
+ render_items(pdf, section[:items])
84
+
85
+ (section[:elements] || []).each do |el|
86
+ pdf.move_down 6
87
+ pdf.font(@style.section_font)
88
+ pdf.text(el[:title], size: (@style.section_size - 3), style: :bold)
89
+ render_items(pdf, el[:items])
90
+ end
91
+ end
92
+ end
93
+
94
+ def render_items(pdf, items)
95
+ return if items.nil? || items.empty?
96
+
97
+ pdf.move_down 4
98
+ pdf.font(@style.body_font)
99
+
100
+ (items || []).each do |item|
101
+ case item[:type]
102
+ when :bullet
103
+ pdf.text("• #{item[:text]}", size: @style.body_size, indent_paragraphs: 16)
104
+ when :paragraph
105
+ pdf.text(item[:text], size: @style.body_size, leading: 2)
106
+ pdf.move_down 4
107
+ else
108
+ # fallback (au cas où)
109
+ pdf.text(item.to_s, size: @style.body_size)
75
110
  end
76
111
  end
77
112
  end
@@ -1,3 +1,3 @@
1
1
  module MyLastCV
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.6"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: my-last-cv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maxime Hanicotte
@@ -23,6 +23,36 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '2.5'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '13.3'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '13.3'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rspec
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.13'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.13'
54
+ description: MyLastCV turns a Markdown CV into a styled PDF using Prawn. Configurable
55
+ styles (fonts, sizes, margins, accent color) and project-level custom fonts support.
26
56
  email:
27
57
  - max_hanicotte@msn.com
28
58
  executables:
@@ -40,7 +70,13 @@ files:
40
70
  homepage: https://www.maxime.hanicotte.net/my-last-cv/
41
71
  licenses:
42
72
  - GPL-3.0-or-later
43
- metadata: {}
73
+ metadata:
74
+ homepage_uri: https://www.maxime.hanicotte.net/my-last-cv/
75
+ source_code_uri: https://github.com/maxime-hanicotte/my-last-cv
76
+ changelog_uri: https://github.com/maxime-hanicotte/my_last_cv/blob/main/CHANGELOG.md
77
+ bug_tracker_uri: https://github.com/maxime-hanicotte/my_last_cv/issues
78
+ documentation_uri: https://github.com/maxime-hanicotte/my_last_cv#readme
79
+ rubygems_mfa_required: 'true'
44
80
  rdoc_options: []
45
81
  require_paths:
46
82
  - lib