Intro:
We will develop a CV generator web app which allows user to generate a PDF CV after user enter his details i.e name, skills, experience bio and click generate button. You can checkout the scshot below:
Folder Structure:
Our folder structure will be like this:
The main folder (You can name it anything), In main folder we have assets folder which contain all the resources used in our project i.e CSS, JS, Images and CV template.
The vendor folder will contain our mPDF library which we will soon install it using composer, The composer.lock and composer.json are auto generated when installing the library.
Finally index.php, which will contain our main view and afcourse PHP script to generate pdf file from user input using mPDF library.
Implementation:
Save the following code in index.php
1. <!DOCTYPE html>
2. <html>
3. <head>
4. <title>Simple CV Generator</title>
5.
6. <!-- Latest compiled and minified CSS -->
7. <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
8.
9. <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
10.
11. <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
12.
13. <!--Custom CSS-->
14. <link rel="stylesheet" type="text/css" href="assests/css/style.css">
15.
16. <!-- Latest compiled and minified JavaScript -->
17.
18. <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
19.
20. <!--Font Awesome-->
21. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
22.
23. <!-- development version, includes helpful console warnings -->
24. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
25.
26. <!-- Custom JS -->
27. <script src="assests/js/cv-form.js"></script>
28.
29. </head>
30. <body>
31.
32. <div class="container">
33. <div class="row">
34. <div class="col-md-6 col-push-6">
35. <div class="jumbotron cv-form">
36. <h3 class=" text-center cv-heading">Simple CV Generator</h3>
37. <p class=" text-center lead">Enter your details and get the CV in PDF format</p>
38. <form method="post" action="<?php htmlspecialchars($_SERVER['PHP_SELF'])?>" class="needs-validation" novalidate id="form1">
39. <div class="form-group">
40. <input name="fname" id="validationCustom01" type="text" placeholder="Name" class="form-control" name="" required>
41. <div class="invalid-feedback">
42. Enter Your Name
43. </div>
44. </div>
45.
46. <div class="form-group">
47. <input name="job" type="text" id="validationCustom02" placeholder="Job Title" class="form-control" required>
48. <div class="invalid-feedback">
49. Enter Job Title
50. </div>
51. </div>
52.
53. <div class="form-group">
54. <textarea name="bio" id="bio" id="validationCustom03" placeholder="Bio" class="form-control" name="" required=""></textarea>
55.
56.
<!--Skills-->
<!--Skills-->
57. <div class="form-group" v-for="(skill,index) in skills">
58. <input type="text" placeholder="Skill" class="form-control" name="skills[]">
59. </div>
60.
61. <div class="form-group">
62. <a href="javaScript:void(0)" v-on:click="addSkill" class="text-left">Add-Skill</a>
63. </div>
64.
65.
66. <!--Experience-->
67. <div class="form-group" v-for="(experience,index) in experiences">
68. <input type="text" placeholder="Experience" class="form-control" name="exps[]">
69. </div>
70. <div class="form-group">
71. <a href="javaScript:void(0)" v-on:click="addExp" class="text-left">Add-Experience</a>
72. </div>
73. <input type="submit" class="btn btn-primary" value="Generate" name="Generate">
74. </form>
75. </div>
76. </div>
77. </div>
78. </div>
79. </body>
80. </html>
The
head section contain external style sheets and javascript, Then In body we have
created a form which contain action to itself ($_SERVER [‘PHP_SELF’] refers to
the name of current file).
We
also have assigned novalidate class to form main tag which will allow us to
show validation message in more custom format instead of tool tips.
The
div containing class
“invalid-feedback” with
each input will contain the error message when the input is left blank
(Remember to add required attribute to input which you want to validate).
Now
for adding skills we want that user should be able to add many skills as much
as he want to show in his CV. So for doing that, we will have single input for
each skill, When user click Add-Skill link each time then one more input
appends to the skills.
We are using vue.js to perform above functionality. As you can see here we have
v-for attribute assigned to skill form group. This is basically a vue.js directive
which allows us to run for loop on the tag. This for loop iterate over the
array, as in our case we have create a array skills in custom.js. This skills
array contains the total skill inputs with their value. By default, skills
array will have a single input, So a
single input will be shown for skill when user first load the page, But when
user click add-skill each time a input is append to the skills variable and
displayed on view.
Now
as user will click a link Add-Skills, so we have to create that clickable link.
So for doing that we have created a form group below the skills form group.
Notice the v-on:click="addSkill" directive used in anchor tag. v-on:click
directive will attach a event listener “addSkill” to the achor tag. AddSkill method will be triggred and will
append a new input to skills array each time it is clicked.
Also
note that we have assigned an array to the name attribute in input tag of skill,
this will allow us to get the skill value of each input in skills array.
Similar functionality is used for experience input.
Now
in assets folder create a folder named “JS” and in that folder create a JS file
named “cv-form.js” and copy paste the below code:
1. window.onload = function () {
2.
3. //Vue.Js Custom
4. var app=new Vue({
5. el:'#form1',
6. data:{
7. skills:[{value:''}],
8. experiences:[{value:''}]
9. },
10. methods:{
11. addSkill(){
12. this.skills.push({value:''});
13. },
14. addExp(){
15. this.experiences.push({value:''});
16. }
17. }
18. });
19.
20.
21. // Example starter JavaScript for disabling form submissions if there are invalid fields
22. (function() {
23. // Fetch all the forms we want to apply custom Bootstrap validation styles to
24. var forms = document.getElementsByClassName('needs-validation');
25. // Loop over them and prevent submission
26. var validation = Array.prototype.filter.call(forms, function(form) {
27. form.addEventListener('submit', function(event) {
28. if (form.checkValidity() === false) {
29. event.preventDefault();
30. event.stopPropagation();
31. }
32. form.classList.add('was-validated');
33. }, true);
34. });
35.
36. })();
37.
38. }
The
Vue JS code is doing what I have
explained above, In VUE constructor we are passing an object which contain the
form id, The data object which contain all the variables and their default
value , The method object which contain addSkill and addExp methods which will
append new value to skills and exp variables respectively as we have discussed
it above.
In
addition to that, we also have another function which is used for bootstrap
validation. Just copy and paste it. This function disables form submissions if
there are invalid fields
For some
custom styling create a folder named “CSS” in assets folder and in that folder
create a file named “style.css” and copy
paste the following styles:
1. body{
2. background:url('../images/bg.png') no-repeat;
3. background-size:100% auto;
4. font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
5.
6. }
7.
8. .cv-form{
9.
10. margin-top:100px;
11. }
12.
13.
14. .lead{
15. font-size: 98%;
16. }
17.
18. .custom-select.is-valid, .form-control.is-valid, .was-validated .custom-select:valid, .was-validated .form-control:valid{
19. border-color: #ced4da;
20. }
We are finished adding client side functionality, Now let’s move ahead to add code that will be executed on server!
But wait, Before writing PHP code we have to
install a library named “mPDF” using composer.This library will do all the
magic i.e generating pdf file from html.
So as
PHP developer, you should know what is composer. But if you don’t know about it
then, let me give you a brief definition.
“A composer is a dependency manager for PHP which will pull all the libraries and their dependency required for your project”.
You can download composer from its official website.
Installing mPDF Library:After downloading and installing composer, let’s move ahead to install mPDF library required for our project using composer
To install mdpf, Open cmd then navigate to the directory which contain your project and use the below composer command:
composer require mpdf/mpdf
This command will install the mPDF lirary inside the vendor folder and will also generate the composer.lock and composer.json files in your project main folder.
(Note: vendor directory will be automatically created by composer; you don’t have to create it)
After installing library, we are ready to go to do the magic!
Add the following PHP code at the bottom of the main index.php file.
You can download composer from its official website.
Installing mPDF Library:After downloading and installing composer, let’s move ahead to install mPDF library required for our project using composer
To install mdpf, Open cmd then navigate to the directory which contain your project and use the below composer command:
composer require mpdf/mpdf
This command will install the mPDF lirary inside the vendor folder and will also generate the composer.lock and composer.json files in your project main folder.
(Note: vendor directory will be automatically created by composer; you don’t have to create it)
After installing library, we are ready to go to do the magic!
Add the following PHP code at the bottom of the main index.php file.
- <?php
- if(isset($_POST['Generate'])){
- require_once __DIR__ . '/vendor/autoload.php';
- ob_start();
- include("assests/templete/html.php");
- $html=ob_get_clean();
- $style = file_get_contents("assests/templete/style.css");
- $mpdf = new \Mpdf\Mpdf();
- $mpdf->WriteHTML($style,1);
- $mpdf->WriteHTML($html,2);
- ob_clean();
- $mpdf->Output();
- }
- ?>
Now, as this library allow us to generate PDF file from html, we have to write the html and styles for our CV. You can do this by your own or you can download html CV templates from internet too or can simply copy the below codes.
To do this, create a folder named “template” in assets folder and create html.php and style.css files in that folder with below codes respectively:
html.php
1. <!DOCTYPE html>
2. <html>
3. <head>
4. <meta name="viewport" content="width=device-width"/>
5. <meta name="description" content="The Curriculum Vitae of Joe Bloggs."/>
6. <meta charset="UTF-8">
7.
8. <link type="text/css" rel="stylesheet" href="style.css">
9. <link href='http://fonts.googleapis.com/css?family=Rokkitt:400,700|Lato:400,300' rel='stylesheet' type='text/css'>
10.
11. <!--[if lt IE 9]>
12. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13. <![endif]-->
14. </head>
15. <body id="top">
16. <div id="cv" class="instaFade">
17. <div class="mainDetails">
18. <div id="headshot" class="quickFade">
19. <img src="headshot.jpg" alt="Alan Smith" />
20. </div>
21.
22. <div id="name">
23. <h1 class="quickFade delayTwo"><?php echo $_POST['fname']; ?></h1>
24. <h2 class="quickFade delayThree"><?php echo $_POST['job']; ?></h2>
25. </div>
26. <div class="clear"></div>
27. </div>
28.
29. <div id="mainArea" class="quickFade delayFive">
30. <section>
31. <article>
32. <div class="sectionTitle">
33. <h1>Personal Profile</h1>
34. </div>
35.
36. <div class="sectionContent">
37. <p><?php echo $_POST['bio']; ?></p>
38. </div>
39. </article>
40. <div class="clear"></div>
41. </section>
42.
43.
44. <section>
45. <div class="sectionTitle">
46. <h1>Work Experience</h1>
47. </div>
48.
49. <div class="sectionContent">
50. <?php foreach($_POST['exps'] as $exp){?>
51. <article>
52. <h3><?php echo $exp; ?></h3>
53. </article>
54. <?php } ?>
55. </div>
56. <div class="clear"></div>
57. </section>
58. <section>
59. <div class="sectionTitle">
60. <h1>Key Skills</h1>
61. </div>
62.
63. <div class="sectionContent">
64. <ul class="keySkills">
65. <?php foreach($_POST['skills'] as $skill){?>
66. <li><?php echo $skill; ?></li>
67. <?php } ?>
68. </ul>
69. </div>
70. <div class="clear"></div>
71. </section>
72. </div>
73. </div>
74.
75. </body>
76. </html>
style.css
1. html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,figcaption,figure,footer,header,hgroup,menu,nav,section,summary,time,mark,audio,video {
2. border:0;
3. font:inherit;
4. font-size:100%;
5. margin:0;
6. padding:0;
7. vertical-align:baseline;
8. }
9.
10. article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section {
11. display:block;
12. }
13.
14. html, body { font-family: 'Lato', helvetica, arial, sans-serif; font-size: 16px; color: #222;}
15.
16. .clear {clear: both;}
17.
18. p {
19. font-size: 1em;
20. line-height: 1.4em;
21. margin-bottom: 20px;
22. color: #444;
23. }
24.
25. #cv {
26. width: 90%;
27. max-width: 800px;
28. background: #f3f3f3;
29. margin: 30px auto;
30. }
31.
32. .mainDetails {
33. padding: 25px 35px;
34. border-bottom: 2px solid #cf8a05;
35. background: #ededed;
36. }
37.
38. #name h1 {
39. font-size: 2.5em;
40. font-weight: 700;
41. font-family: 'Rokkitt', Helvetica, Arial, sans-serif;
42. margin-bottom: -6px;
43. }
44.
45. #name h2 {
46. font-size: 2em;
47. margin-left: 2px;
48. font-family: 'Rokkitt', Helvetica, Arial, sans-serif;
49. }
50.
51. #mainArea {
52. padding: 0 40px;
53. }
54.
55. #headshot {
56. width: 12.5%;
57. float: left;
58. margin-right: 30px;
59. }
60.
61. #headshot img {
62. width: 100%;
63. height: auto;
64. -webkit-border-radius: 50px;
65. border-radius: 50px;
66. }
67.
68. #name {
69. float: left;
70. }
71.
72. #contactDetails {
73. float: right;
74. }
75.
76. #contactDetails ul {
77. list-style-type: none;
78. font-size: 0.9em;
79. margin-top: 2px;
80. }
81.
82. #contactDetails ul li {
83. margin-bottom: 3px;
84. color: #444;
85. }
86.
87. #contactDetails ul li a, a[href^=tel] {
88. color: #444;
89. text-decoration: none;
90. -webkit-transition: all .3s ease-in;
91. -moz-transition: all .3s ease-in;
92. -o-transition: all .3s ease-in;
93. -ms-transition: all .3s ease-in;
94. transition: all .3s ease-in;
95. }
96.
97. #contactDetails ul li a:hover {
98. color: #cf8a05;
99. }
100.
101.
102.
section {
103.
border-top: 1px solid #dedede;
104.
padding: 20px 0 0;
105.
}
106.
107.
section:first-child {
108.
border-top: 0;
109.
}
110.
111.
section:last-child {
112.
padding: 20px 0 10px;
113.
}
114.
115.
.sectionTitle {
116.
float: left;
117.
width: 25%;
118.
}
119.
120.
.sectionContent {
121.
float: right;
122.
width: 72.5%;
123.
}
124.
125.
.sectionTitle h1 {
126.
font-family: 'Rokkitt', Helvetica, Arial, sans-serif;
127.
font-style: italic;
128.
font-size: 1.5em;
129.
color: #cf8a05;
130.
}
131.
132.
.sectionContent h2 {
133.
font-family: 'Rokkitt', Helvetica, Arial, sans-serif;
134.
font-size: 1.5em;
135.
margin-bottom: -2px;
136.
}
137.
138.
.subDetails {
139.
font-size: 0.8em;
140.
font-style: italic;
141.
margin-bottom: 3px;
142.
}
143.
144.
.keySkills {
145.
list-style-type: none;
146.
-moz-column-count:3;
147.
-webkit-column-count:3;
148.
column-count:3;
149.
margin-bottom: 20px;
150.
font-size: 1em;
151.
color: #444;
152.
}
153.
154.
.keySkills ul li {
155.
margin-bottom: 3px;
156.
}
157.
158.
@media all and (min-width: 602px) and (max-width: 800px) {
159.
#headshot {
160.
display: none;
161.
}
162.
163.
.keySkills {
164.
-moz-column-count:2;
165.
-webkit-column-count:2;
166.
column-count:2;
167.
}
168.
}
169.
170.
@media all and (max-width: 601px) {
171.
#cv {
172.
width: 95%;
173.
margin: 10px auto;
174.
min-width: 280px;
175.
}
176.
177.
#headshot {
178.
display: none;
179.
}
180.
181.
#name, #contactDetails {
182.
float: none;
183.
width: 100%;
184.
text-align: center;
185.
}
186.
187.
.sectionTitle, .sectionContent {
188.
float: none;
189.
width: 100%;
190.
}
191.
192.
.sectionTitle {
193.
margin-left: -2px;
194.
font-size: 1.25em;
195.
}
196.
197.
.keySkills {
198.
-moz-column-count:2;
199.
-webkit-column-count:2;
200.
column-count:2;
201.
}
202.
}
203.
204.
205.
.quickFade {
206.
-webkit-animation-name: reset, fade-in;
207.
-webkit-animation-duration: 2.5s;
208.
-webkit-animation-timing-function: ease-in;
209.
210.
-moz-animation-name: reset, fade-in;
211.
-moz-animation-duration: 2.5s;
212.
-moz-animation-timing-function: ease-in;
213.
214.
animation-name: reset, fade-in;
215.
animation-duration: 2.5s;
216.
animation-timing-function: ease-in;
217.
}
218.
219.
.delayOne {
220.
-webkit-animation-delay: 0, .5s;
221.
-moz-animation-delay: 0, .5s;
222.
animation-delay: 0, .5s;
223.
}
224.
225.
.delayTwo {
226.
-webkit-animation-delay: 0, 1s;
227.
-moz-animation-delay: 0, 1s;
228.
animation-delay: 0, 1s;
229.
}
230.
231.
.delayThree {
232.
-webkit-animation-delay: 0, 1.5s;
233.
-moz-animation-delay: 0, 1.5s;
234.
animation-delay: 0, 1.5s;
235.
}
236.
237.
.delayFour {
238.
-webkit-animation-delay: 0, 2s;
239.
-moz-animation-delay: 0, 2s;
240.
animation-delay: 0, 2s;
241.
}
242.
243.
.delayFive {
244.
-webkit-animation-delay: 0, 2.5s;
245.
-moz-animation-delay: 0, 2.5s;
246.
animation-delay: 0, 2.5s;
247.
}
You can notice that in html.php file, we are capturing the user inputs to display them in CV using the PHP Post array.
Also Read: How Access and Handle Form Data in PHP (POST And GET)
So in PHP, Firstly we are checking to make sure that submit button is clicked before the code executes by using isset function.
Now we have to get the html and styles from the files we created above, because mPDF library will use them to generate a PDF file.
Since we are using PHP variables in html.php, we cannot use file_get_contents method to get all the html, because this method will convert all text even the PHP code in string. So to overcome this issue, we can use the concept of output buffering.
We will get the html by simply including the html.php file using PHP include function. But instead of returning the content from html.php, We are storing the contents of html.php in buffer memory.
To tell PHP about which content to buffer we have to use ob_start() before the buffered content. Now we can simply return the buffered content in $html variable using ob_get_clean. This method will return the buffer content and clean the buffer memory.
Since style.css is simply a text, we can get it using file_get_contents().
Now we have to pass the html and style variables to the mpDF library writeHtml method. After passing the content, we are simply calling the method “$mpdf->output”, It will send the content in downloadable PDF format.
Here is example of what PDF file will contain:
Here is example of what PDF file will contain:
Want to add more functionality? Download complete source code and add your customization!
Sharing is Caring, Share this Article to Your Coding Community, Thanks.
No comments:
Post a Comment