UPDATE export from electroshot to puppeteer
0
.travis.yml
Normal file → Executable file
6
DEVELOPER.md
Normal file → Executable file
@ -31,7 +31,9 @@ Your new resume will be now reachable at localhost:8080/#/resume/TEMPLATE-NAME.
|
|||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
4. Generate previews with `npm run preview`. PDF- and PNG-files will be generated.
|
5. Export resumes as PDF with `npm run export`. Verify export of new template.
|
||||||
|
|
||||||
|
4. Generate previews by converting PDF-files to PNG-files with `npm run preview`.
|
||||||
|
|
||||||
5. Add preview to `/src/pages/home.vue`:
|
5. Add preview to `/src/pages/home.vue`:
|
||||||
```javascript
|
```javascript
|
||||||
@ -73,7 +75,7 @@ All fonts are installed via npm. To add a new font, search for the associated np
|
|||||||
|
|
||||||
## Export
|
## Export
|
||||||
|
|
||||||
Resumes are being exported with [Electroshot](https://github.com/mixu/electroshot).
|
Resumes are being exported with [Puppeteer](https://github.com/GoogleChrome/puppeteer).
|
||||||
|
|
||||||
### Box Shadows
|
### Box Shadows
|
||||||
|
|
||||||
|
|||||||
0
ISSUE_TEMPLATE.md
Normal file → Executable file
0
LICENCE.md
Normal file → Executable file
0
PULL_REQUEST_TEMPLATE.md
Normal file → Executable file
@ -68,8 +68,8 @@ Feel free to add your own templates, language supports, fix bugs or improve the
|
|||||||
This project uses several open source packages:
|
This project uses several open source packages:
|
||||||
|
|
||||||
* <a href="https://github.com/vuejs/vue" target="_blank">Vue</a>
|
* <a href="https://github.com/vuejs/vue" target="_blank">Vue</a>
|
||||||
|
* <a href="https://github.com/GoogleChrome/puppeteer" target="_blank">Puppeteer</a>
|
||||||
* <a href="https://github.com/less/less.js" target="_blank">LESS</a>
|
* <a href="https://github.com/less/less.js" target="_blank">LESS</a>
|
||||||
* <a href="https://github.com/mixu/electroshot" target="_blank">Electroshot</a>
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|||||||
0
build/webpack.prod.conf.js
Normal file → Executable file
33
node/app.js
@ -1,7 +1,8 @@
|
|||||||
|
const puppeteer = require('puppeteer');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const exec = require('child_process').exec;
|
|
||||||
const Rx = require('rxjs/Rx');
|
const Rx = require('rxjs/Rx');
|
||||||
|
const isRoot = require('is-root');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
|
||||||
const fetchResponse = () => {
|
const fetchResponse = () => {
|
||||||
@ -36,26 +37,28 @@ const timedOut = timeout => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const convert = async() => {
|
const convert = async() => {
|
||||||
|
if(isRoot()) {
|
||||||
|
console.log('ERROR: Please run this without root (admin) permissions.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
await waitForServerReachable().first().toPromise();
|
await waitForServerReachable().first().toPromise();
|
||||||
console.log('Connected to server ...');
|
console.log('Connected to server ...');
|
||||||
console.log('Exporting ...');
|
console.log('Exporting ...');
|
||||||
try {
|
try {
|
||||||
const directories = getResumesFromDirectories();
|
const directories = getResumesFromDirectories();
|
||||||
const scripts = directories.map(resume => electroshotScript(resume.path));
|
directories.forEach(async(dir) => {
|
||||||
await execBash(scripts.join(' && '));
|
const browser = await puppeteer.launch();
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.goto('http://localhost:8080/#/resume/' + dir.name, {waitUntil: 'networkidle'});
|
||||||
|
await page.pdf({path: path.join(__dirname, '../pdf/' + dir.name + '.pdf'), format: 'A4'});
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(err);
|
throw new Error(err);
|
||||||
}
|
}
|
||||||
console.log('Finished exports.');
|
console.log('Finished exports.');
|
||||||
};
|
};
|
||||||
|
|
||||||
const electroshotScript = resume => {
|
|
||||||
const dir = path.join(__dirname, '../pdf');
|
|
||||||
return 'electroshot localhost:8080/#/resume/' + resume +
|
|
||||||
' 2481x3508 --pdf-margin none --format pdf --out ' + dir +
|
|
||||||
' --filename "' + resume + '.pdf" --pdf-background';
|
|
||||||
};
|
|
||||||
|
|
||||||
const getResumesFromDirectories = () => {
|
const getResumesFromDirectories = () => {
|
||||||
const directories = getDirectories();
|
const directories = getDirectories();
|
||||||
return directories
|
return directories
|
||||||
@ -74,14 +77,4 @@ const getDirectories = () => {
|
|||||||
.filter(file => file !== 'resumes.js' && file !== 'template.vue');
|
.filter(file => file !== 'resumes.js' && file !== 'template.vue');
|
||||||
};
|
};
|
||||||
|
|
||||||
const execBash = script => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
exec(script,
|
|
||||||
error => {
|
|
||||||
if (error) reject(error);
|
|
||||||
else resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
convert();
|
convert();
|
||||||
|
|||||||
0
node/preview.js
Normal file → Executable file
@ -13,7 +13,7 @@
|
|||||||
"build": "node build/build.js",
|
"build": "node build/build.js",
|
||||||
"start": "node build/dev-server.js",
|
"start": "node build/dev-server.js",
|
||||||
"pdf": "node node/app.js",
|
"pdf": "node node/app.js",
|
||||||
"preview": "npm run export && node node/preview.js",
|
"preview": "node node/preview.js",
|
||||||
"template": "node node/template/template.js",
|
"template": "node node/template/template.js",
|
||||||
"export": "concurrently \"npm run dev\" \"npm run pdf\" --success first --kill-others --raw",
|
"export": "concurrently \"npm run dev\" \"npm run pdf\" --success first --kill-others --raw",
|
||||||
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs"
|
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs"
|
||||||
@ -46,7 +46,6 @@
|
|||||||
"copy-webpack-plugin": "^4.0.1",
|
"copy-webpack-plugin": "^4.0.1",
|
||||||
"cpx": "^1.5.0",
|
"cpx": "^1.5.0",
|
||||||
"css-loader": "^0.28.0",
|
"css-loader": "^0.28.0",
|
||||||
"electroshot": "^1.4.0",
|
|
||||||
"eslint": "^4.6.1",
|
"eslint": "^4.6.1",
|
||||||
"eslint-config-standard": "^10.2.1",
|
"eslint-config-standard": "^10.2.1",
|
||||||
"eslint-friendly-formatter": "^3.0.0",
|
"eslint-friendly-formatter": "^3.0.0",
|
||||||
@ -64,6 +63,7 @@
|
|||||||
"html-webpack-plugin": "^2.30.1",
|
"html-webpack-plugin": "^2.30.1",
|
||||||
"http": "0.0.0",
|
"http": "0.0.0",
|
||||||
"http-proxy-middleware": "^0.17.3",
|
"http-proxy-middleware": "^0.17.3",
|
||||||
|
"is-root": "^1.0.0",
|
||||||
"less": "^2.7.2",
|
"less": "^2.7.2",
|
||||||
"less-loader": "^4.0.5",
|
"less-loader": "^4.0.5",
|
||||||
"lolex": "^1.5.2",
|
"lolex": "^1.5.2",
|
||||||
@ -74,6 +74,7 @@
|
|||||||
"pdf-image": "^1.1.0",
|
"pdf-image": "^1.1.0",
|
||||||
"postcss": "^6.0.11",
|
"postcss": "^6.0.11",
|
||||||
"postcss-cssnext": "^2.11.0",
|
"postcss-cssnext": "^2.11.0",
|
||||||
|
"puppeteer": "^0.12.0",
|
||||||
"rename": "^1.0.4",
|
"rename": "^1.0.4",
|
||||||
"request": "^2.81.0",
|
"request": "^2.81.0",
|
||||||
"request-promise": "^4.2.1",
|
"request-promise": "^4.2.1",
|
||||||
@ -94,7 +95,7 @@
|
|||||||
"webpack-merge": "^4.1.0"
|
"webpack-merge": "^4.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 7.4.0",
|
"node": ">= 8.0.0",
|
||||||
"npm": ">= 5.0.0"
|
"npm": ">= 5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
pdf/oblique.pdf
BIN
pdf/purple.pdf
BIN
pdf/side-bar.pdf
0
src/assets/logo.png
Normal file → Executable file
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
0
src/assets/profile-images/girl.png
Normal file → Executable file
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
0
src/assets/profile-images/guy.png
Normal file → Executable file
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
0
src/lang/cn.js
Normal file → Executable file
0
src/lang/de.js
Normal file → Executable file
0
src/lang/en.js
Normal file → Executable file
0
src/lang/es.js
Normal file → Executable file
0
src/lang/fr.js
Normal file → Executable file
0
src/lang/he.js
Normal file → Executable file
0
src/lang/hu.js
Normal file → Executable file
0
src/lang/id.js
Normal file → Executable file
0
src/lang/it.js
Normal file → Executable file
0
src/lang/ja.js
Normal file → Executable file
0
src/lang/nl.js
Normal file → Executable file
0
src/lang/pl.js
Normal file → Executable file
0
src/lang/pt-br.js
Normal file → Executable file
0
src/lang/pt.js
Normal file → Executable file
0
src/lang/ru.js
Normal file → Executable file
0
src/lang/sv.js
Normal file → Executable file
0
src/lang/th.js
Normal file → Executable file
@ -1,49 +0,0 @@
|
|||||||
|
|
||||||
const getElements = elements => {
|
|
||||||
if (elements && elements.length > 0) return elements;
|
|
||||||
elements = document.getElementsByTagName('*');
|
|
||||||
let ret = [];
|
|
||||||
for (const element of elements) {
|
|
||||||
const style = window.getComputedStyle(element, null).getPropertyValue('box-shadow');
|
|
||||||
if (style !== 'none') {
|
|
||||||
element.style.boxShadow = 'none';
|
|
||||||
ret.push({
|
|
||||||
shadow: style,
|
|
||||||
top: element.offsetTop + 'px',
|
|
||||||
left: element.offsetLeft + 'px',
|
|
||||||
width: element.getBoundingClientRect().width + 'px',
|
|
||||||
height: element.getBoundingClientRect().height + 'px'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateElements = elements => {
|
|
||||||
let ret = '<div id="chrome-shadow-fixer">';
|
|
||||||
elements.forEach((el, i) => (ret += '<div id="chrome-shadow-fixer-' + i + '"></div>'));
|
|
||||||
ret += '</div>';
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fix = elements => {
|
|
||||||
const elementsWithShadows = getElements(elements);
|
|
||||||
if (elementsWithShadows.length < 1) {
|
|
||||||
console.warn('fixShadows(): No elements to fix shadows.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const generatedElements = generateElements(elementsWithShadows);
|
|
||||||
document.body.innerHTML += generatedElements;
|
|
||||||
elementsWithShadows.forEach((el, i) => {
|
|
||||||
const element = document.querySelector('#chrome-shadow-fixer-' + i);
|
|
||||||
element.style.height = el.height;
|
|
||||||
element.style.width = el.width;
|
|
||||||
element.style.left = el.left;
|
|
||||||
element.style.top = el.top;
|
|
||||||
element.style.position = 'absolute';
|
|
||||||
element.style.boxShadow = el.shadow;
|
|
||||||
element.style['-webkit-print-color-adjust'] = 'exact';
|
|
||||||
element.style['-webkit-filter'] = 'opacity(1)';
|
|
||||||
});
|
|
||||||
return elementsWithShadows;
|
|
||||||
};
|
|
||||||
@ -10,18 +10,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
// import * as chromeShadowFixer from 'chrome-shadow-fixer';
|
|
||||||
import * as chromeShadowFixer from './chromeShadowFixer';
|
|
||||||
import '../resumes/resumes';
|
import '../resumes/resumes';
|
||||||
export default Vue.component('resume', {
|
export default Vue.component('resume', {
|
||||||
name: 'app',
|
name: 'app'
|
||||||
mounted: () => {
|
|
||||||
const els = chromeShadowFixer.fix();
|
|
||||||
if (
|
|
||||||
els &&
|
|
||||||
els.length > 0
|
|
||||||
) window.onhashchange = () => { location.reload(); };
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||