.editorconfig000066400000000000000000000006401516104167300135370ustar00rootroot00000000000000# EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] # Change these settings to your own preference indent_style = space indent_size = 4 # We recommend you to keep these unchanged end_of_line = lf charset = utf-8 trim_trailing_whitespace = false insert_final_newline = true [*.md] trim_trailing_whitespace = false .github/000077500000000000000000000000001516104167300124225ustar00rootroot00000000000000.github/workflows/000077500000000000000000000000001516104167300144575ustar00rootroot00000000000000.github/workflows/build.yml000066400000000000000000000016761516104167300163130ustar00rootroot00000000000000# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions name: build on: schedule: - cron: '0 0 1 * *' push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: ['lts/*'] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm i -g grunt-cli - run: npm install - run: grunt .gitignore000066400000000000000000000003401516104167300130470ustar00rootroot00000000000000/build /node_modules /*.log .idea/workspace.xml .idea/tasks.xml .idea/profiles_settings.xml .idea/inspectionProfiles/Project_Default.xml .idea/inspectionProfiles/profiles_settings.xml node_modules/.yarn-integrity /release.idea/000077500000000000000000000000001516104167300120425ustar00rootroot00000000000000.idea/codeStyleSettings.xml000066400000000000000000000004251516104167300162410ustar00rootroot00000000000000 .idea/html-pdf.iml000066400000000000000000000005201516104167300142550ustar00rootroot00000000000000 .idea/misc.xml000066400000000000000000000006111516104167300135150ustar00rootroot00000000000000 true false true .idea/modules.xml000066400000000000000000000004141516104167300142330ustar00rootroot00000000000000 .idea/php.xml000066400000000000000000000004441516104167300133550ustar00rootroot00000000000000 .idea/vcs.xml000066400000000000000000000002471516104167300133620ustar00rootroot00000000000000 .npmignore000066400000000000000000000002431516104167300130600ustar00rootroot00000000000000/.idea /artifacts /build /test /node_modules /*.iml /*.ipr /*.iws /.travis.yml /.scrutinizer.yml /Gruntfile.js /*.lock *.log /corifeus-boot.json /release /.github .travis.yml000066400000000000000000000014571516104167300132020ustar00rootroot00000000000000language: node_js cache: npm: false node_js: - lts/* before_script: - npm install -g grunt-cli npm env: global: secure: Mcnk2Q/XmDOEtGMFepDXlSIJ+bjUJdPNahduWUqjoKUjyj5ujjG90MX+iF8qk+6VzrJ/d+0mPGS2rx+g+oT5/4Vbg0MYclSvxQ+idK6cmCAFEGzj8u/0vNsDQpf9ncIecesuriXTi9L2yd17rSaX3iNERoYW8o86RVXwksk3nPZql/AIB692qjjpS0mopQdPJl9rc0EtSRsc/lj/FQ5+zlJqyK/3Hi+OtvbL3DCu6yE6NXZ2PCgbCSidauqjfwh3+q/548dsxgRsmDjaLXru17FUAR3/YgL/Q9nrkA3G0iv9D+mOdkD7WMYBusm7m1ocazssfcYbs64XAAz2JOZ3HeN+4TN27NGP59I9mtCq5UHP1JI27QviQXRqrmo4StBeimUnypuKy7IUJGF8VBzfe2aWZa+1/FQBli52yUVrKwYJwVLdSePzjDhgOlAtS63XzrXLmw3rGS1XrQytbRUG84BZACHvR1DYXmB9PWoqKeHzNPEceXOw1cqAjDaegA69Fx/B9POh1V2w/B1SPsvWMJq3EQjdfb6wpy824yJyUCJINMcW+O5F5yL/JpaG2dMLiJsBw6/lHko+Fsdo0j4YWsGpV25EXXyUewpAf8FOXtieMtVWV5MxCA3q11o/apEWqs9OHlRh9KukFJsiFMxeOUhb6B5gOXJeNTN7iBGrawg= Gruntfile.js000066400000000000000000000004541516104167300133620ustar00rootroot00000000000000module.exports = (grunt) => { const builder = require(`corifeus-builder`); const loader = new builder.loader(grunt); loader.js({ replacer: { type: 'p3x', npmio: true, }, }); grunt.registerTask('default', builder.config.task.build.js); };LICENSE000066400000000000000000000023511516104167300120700ustar00rootroot00000000000000 @license p3x-html-pdf v2022.4.129 📃 Generates PDF from HTML with custom headers and footers with wkhtmltopdf https://corifeus.com/html-pdf Copyright (c) 2022 Patrik Laszlo / P3X / Corifeus and contributors. MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. README.md000066400000000000000000000057121516104167300123460ustar00rootroot00000000000000[//]: #@corifeus-header [![NPM](https://nodei.co/npm/p3x-html-pdf.png?downloads=true&downloadRank=true)](https://www.npmjs.com/package/p3x-html-pdf/) [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://paypal.me/patrikx3) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Corifeus @ Facebook](https://img.shields.io/badge/Facebook-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![Build Status](https://github.com/patrikx3/html-pdf/workflows/build/badge.svg)](https://github.com/patrikx3/html-pdf/actions?query=workflow%3Abuild) [![Uptime Robot ratio (30 days)](https://img.shields.io/uptimerobot/ratio/m780749701-41bcade28c1ea8154eda7cca.svg)](https://stats.uptimerobot.com/9ggnzcWrw) # 📃 Generates PDF from HTML with custom headers and footers with wkhtmltopdf v2022.4.129 **Bugs are evident™ - MATRIX️** ### NodeJS LTS is supported ### Built on NodeJs version ```txt v16.15.0 ``` # Description [//]: #@corifeus-header:end Generates PDF from HTML with custom headers and footers with wkhtmltopdf # First version Since, the basic installation, we need scripts to startup the library: ```bash npm install p3x-html-pdf ``` Soon, it will work with OSX as well. It is a ```microservice``` that I created for my job with Sygnus. Given of the amount of code I write, sometimes it takes to write documentation. [//]: #@corifeus-footer --- 🙏 This is an open-source project. Star this repository, if you like it, or even donate to maintain the servers and the development. Thank you so much! Possible, this server, rarely, is down, please, hang on for 15-30 minutes and the server will be back up. All my domains ([patrikx3.com](https://patrikx3.com) and [corifeus.com](https://corifeus.com)) could have minor errors, since I am developing in my free time. However, it is usually stable. **Note about versioning:** Versions are cut in Major.Minor.Patch schema. Major is always the current year. Minor is either 4 (January - June) or 10 (July - December). Patch is incremental by every build. If there is a breaking change, it should be noted in the readme. --- [**P3X-HTML-PDF**](https://corifeus.com/html-pdf) Build v2022.4.129 [![Donate for Corifeus / P3X](https://img.shields.io/badge/Donate-Corifeus-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) ## P3X Sponsor [IntelliJ - The most intelligent Java IDE](https://www.jetbrains.com/?from=patrikx3) [![JetBrains](https://cdn.corifeus.com/assets/svg/jetbrains-logo.svg)](https://www.jetbrains.com/?from=patrikx3) [//]: #@corifeus-footer:end html-pdf.iml000066400000000000000000000006241516104167300133020ustar00rootroot00000000000000 package.json000066400000000000000000000024401516104167300133500ustar00rootroot00000000000000{ "name": "p3x-html-pdf", "version": "2022.4.129", "corifeus": { "prefix": "p3x-", "publish": true, "type": "p3x", "code": "Venture", "nodejs": "v16.15.0", "opencollective": false, "reponame": "html-pdf", "build": true }, "license": "MIT", "description": "📃 Generates PDF from HTML with custom headers and footers with wkhtmltopdf", "main": "src/index.js", "directories": { "test": "test" }, "scripts": { "test": "grunt", "install": "node src/package/install.js" }, "repository": { "type": "git", "url": "git+https://github.com/patrikx3/html-pdf.git" }, "keywords": [ "html", "pdf", "async", "header", "custom", "footer" ], "author": "Patrik Laszlo ", "bugs": { "url": "https://github.com/patrikx3/html-pdf/issues" }, "homepage": "https://corifeus.com/html-pdf", "devDependencies": { "corifeus-builder": "^2022.4.130" }, "dependencies": { "cheerio": "^1.0.0-rc.11", "corifeus-utils": "^2022.4.130", "fs-extra": "^10.1.0", "progress": "^2.0.3" }, "engines": { "node": ">=12.13.0" } }src/000077500000000000000000000000001516104167300116515ustar00rootroot00000000000000src/base.html000066400000000000000000000007141516104167300134530ustar00rootroot00000000000000 ${settings.html} src/header-footer.html000066400000000000000000000026231516104167300152660ustar00rootroot00000000000000 src/index.js000066400000000000000000000161231516104167300133210ustar00rootroot00000000000000const utils = require('corifeus-utils'); const path = require('path'); const fs = require('mz/fs'); const fsExtra = require('fs-extra'); const template = require('lodash/template'); const cleanDeep = require('lodash/cloneDeep'); const cheerio = require('cheerio') const startCase = require('lodash/startCase'); const os = require('os'); const isWin = os.platform() === 'win32'; const binPathAddon = isWin ? '' : 'wkhtmltox/'; const binpath = path.resolve(`${__dirname}/../release/${binPathAddon}bin/wkhtmltopdf`); const generate = async (options) => { const {settings, saveFile, base, debug, title} = options; let {javascriptDelay} = options; const isFixed = () => { return settings.template.fixedWidth > 0 && settings.template.fixedHeight } const save = settings.hasOwnProperty('save') && settings.save === true; // const consoleDebugOriginal = console.debug; if (debug) { console.debug = console.info; } else { console.debug = () => { }; } let tmpHtmlPath; let tmpPdfPath; let tmpHtmlPathFooter; let tmpHtmlPathHeader; try { const baseHtml = (await fs.readFile(`${__dirname}/base.html`)).toString(); const baseHtmlFooterHeader = (await fs.readFile(`${__dirname}/header-footer.html`)).toString(); let html = template(baseHtml)(options) let $ = cheerio.load(html); const $id = $('[id]'); let header = {} let footer = {}; const defaultHeightMargin = '10mm'; let marginTop = defaultHeightMargin; let marginBottom = defaultHeightMargin; $id.each((index, element) => { const $element = $(element) //const $parent = $element.parent(); // console.debug($parent.html()); const id = $element.attr('id'); if (id.startsWith('p3x-header')) { header[id] = `
${$element.html()}
`; marginTop = $element.data('height') || defaultHeightMargin; $element.remove(); } else if (id.startsWith('p3x-footer')) { footer[id] = `
${$element.html()}
`; marginBottom = $element.data('height') || defaultHeightMargin; $element.remove(); } }) // console.debug('marginTop', marginTop); // console.debug('marginBottom', marginBottom); /* page: frompage: topage: = pages webpage: section: subsection: date: isodate: time: title: doctitle: sitepage: sitepages: */ $('.p3x-header').remove(); $('.p3x-footer').remove(); //$('ngivr-core-pdf-template-include').remove() //$('ngivr-html-template-include').remove() html = $.html(); html = html.replace(/\${qr}/g, options.settings.qr) const headerOrFooter = (data, type) => { const lodashTemplateHack = ` item = item.replace(/\\$\{page}/g, vars.page); item = item.replace(/\\$\{pages}/g, vars.pages); item = item.replace(/\\$\{qr}/g, qr); `; const mainsSettings = cleanDeep(options) mainsSettings.settings.html = JSON.stringify(data, null, 4); data = template(baseHtmlFooterHeader)(mainsSettings) data = data.replace('// headerOrFooter //', `var headerOrFooter = ${type};`) data = data.replace('// lodash-template-hack //', lodashTemplateHack) data = data.replace('// qr-hack //', ` var qr = ${JSON.stringify(mainsSettings.settings.qr)}; `) return { mainSettings: mainsSettings, data: data } } const headResult = headerOrFooter(header, 1) // const headerSettings = headResult.mainSettings; header = headResult.data; const footResult = headerOrFooter(footer, 2) // const footerSettings = footResult.mainSettings; footer = footResult.data; // console.debug('headerSettings', headerSettings) // console.debug('footerSettings', footerSettings) // console.debug('html', html) // console.debug('header', header) tmpHtmlPath = await utils.fs.ensureTempFile(html, 'html') tmpHtmlPathHeader = await utils.fs.ensureTempFile(header, 'html') tmpHtmlPathFooter = await utils.fs.ensureTempFile(footer, 'html') tmpPdfPath = await utils.fs.tempFileName('pdf'); // console.debug('footer', footer) // console.debug('tmpHtmlPath', tmpHtmlPath); // console.debug('tmpPdfPath', tmpPdfPath); // --header-html ${tmpHtmlPath} --footer-html ${tmpHtmlPath} /* -B, --margin-bottom Set the page bottom margin -L, --margin-left Set the page left margin (default 10mm) -R, --margin-right Set the page right margin (default 10mm) -T, --margin-top Set the page top margin */ // // if (javascriptDelay === undefined) { javascriptDelay = 1000; } let addOn = ''; if (isFixed()) { marginTop = '0mm'; marginBottom = '0mm'; addOn += ` --margin-left 0mm --margin-right 0mm --disable-smart-shrinking` } const pageSize = isFixed() ? `--page-width ${settings.template.fixedWidth + 1}mm --page-height ${settings.template.fixedHeight + 1}mm` : `--page-size ${settings.template.format}`; const generatePdfCommand = `${binpath} --javascript-delay ${javascriptDelay} --copies ${settings.template.copies} --margin-bottom ${marginBottom} --margin-top ${marginTop} ${addOn} ${debug ? '--debug-javascript' : ''} --title ${JSON.stringify(title + ' ' + new Date().toLocaleString())} --orientation ${startCase(settings.template.orientation)} ${pageSize} ${tmpHtmlPath} --header-html ${tmpHtmlPathHeader} --footer-html ${tmpHtmlPathFooter} ${tmpPdfPath}`; console.debug('generatePdfCommand', generatePdfCommand); await utils.childProcess.exec(generatePdfCommand, debug); if (save) { await fsExtra.move(tmpPdfPath, saveFile) } else { return fs.readFile(tmpPdfPath); } } finally { if (tmpHtmlPath !== undefined) { await fsExtra.remove(tmpHtmlPath); } if (tmpHtmlPathFooter !== undefined) { await fsExtra.remove(tmpHtmlPathFooter); } if (tmpHtmlPathHeader !== undefined) { await fsExtra.remove(tmpHtmlPathHeader); } if (!save && tmpPdfPath !== undefined) { await fsExtra.remove(tmpPdfPath); } // console.debug = consoleDebugOriginal; } } module.exports.generate = generate; src/package/000077500000000000000000000000001516104167300132445ustar00rootroot00000000000000src/package/install.js000066400000000000000000000130301516104167300152450ustar00rootroot00000000000000const utils = require('corifeus-utils'); const os = require('os'); const path = require('path'); const fsExtra = require('fs-extra'); // https://api.github.com/repos/wkhtmltopdf/wkhtmltopdf/tags // https://api.github.com/repos/wkhtmltopdf/wkhtmltopdf/releases/latest const progress = require('progress'); //const currentRelease = 'https://api.github.com/repos/wkhtmltopdf/wkhtmltopdf/releases/latest'; const currentRelease = 'https://api.github.com/repos/wkhtmltopdf/wkhtmltopdf/releases/4730156'; const install = async () => { let staticInstaller; const staticInstallerPath = `${process.cwd()}/package.json`; staticInstaller = require(staticInstallerPath) // console.log('staticInstaller 1', JSON.stringify(staticInstaller, null, 4 )) if (staticInstaller.hasOwnProperty('_where')) { staticInstaller = require(`${staticInstaller._where}/package.json`) } // console.log('staticInstaller 2', JSON.stringify(staticInstaller, null, 4 )) if (typeof staticInstaller === 'object' && staticInstaller.hasOwnProperty('p3x') && staticInstaller.p3x.hasOwnProperty('installer') && staticInstaller.p3x.installer['p3x-html-pdf'].hasOwnProperty('linux-x64') && staticInstaller.p3x.installer['p3x-html-pdf'].hasOwnProperty('win32-x64')) { console.log('Found static download instead of GitHub as:', ` `, JSON.stringify(staticInstaller.p3x.installer['p3x-html-pdf'], null, 4)) } else { staticInstaller = undefined; } const binPath = path.resolve(`${__dirname}/../../release`); await fsExtra.remove(binPath); const arch = os.arch(); const platform = os.platform() let platformSearch; let archSearch; if (platform === 'linux') { platformSearch = 'linux'; if (arch === 'x64') { archSearch = 'amd64'; } } else if (platform === 'win32' && arch === 'x64') { // wkhtmltox-0.12.4_mingw-w64-cross-win64.exe platformSearch = 'mingw-w64-cross'; archSearch = 'win64'; } if (platformSearch === undefined || archSearch === undefined) { console.log(`This platform for p3x-html-pdf is not implemented: ${platform}/${arch}`); console.log('The p3x-html-pdf will not work, but will work silently'); return } console.log(`Found platform: ${platformSearch} and architecture ${archSearch}`); console.log(`Downloading latest release for wkhtmltopdf`); await utils.fs.ensureDir(binPath); let releaseBinaryFileName let downloadResponse let releaseBinaryFile if (staticInstaller === undefined) { const headers = {}; if (process.env.hasOwnProperty('GITHUB_TOKEN')) { headers['Authorization'] = `token ${process.env.GITHUB_TOKEN}` console.log(`Found GITHUB_TOKEN, using as GitHub authorization.`) } const {body} = await utils.http.request({ url: currentRelease, headers: headers }); const findReleaseAsset = body.assets.find((asset) => { return asset.browser_download_url.includes(platformSearch) && asset.browser_download_url.includes(archSearch); }) if (findReleaseAsset === undefined || findReleaseAsset === null) { throw new Error('Could not found the latest release for wkhtmltopdf') } console.log(`Found the latest release: ${findReleaseAsset.browser_download_url}`); releaseBinaryFileName = `${binPath}/${path.basename(findReleaseAsset.browser_download_url)}`; downloadResponse = await utils.http.request({ url: findReleaseAsset.browser_download_url, pipe: true, }) releaseBinaryFile = utils.fs.createWriteStream(releaseBinaryFileName); downloadResponse.pipe(releaseBinaryFile.stream); } else { console.log(`Found the latest release: ${staticInstaller.p3x.installer['p3x-html-pdf'][`${platform}-${arch}`]}`) releaseBinaryFileName = `${binPath}/${path.basename(staticInstaller.p3x.installer['p3x-html-pdf'][`${platform}-${arch}`])}`; let fixedUrl = staticInstaller.p3x.installer['p3x-html-pdf'][`${platform}-${arch}`] downloadResponse = await utils.http.request({ url: fixedUrl, pipe: true, }) releaseBinaryFile = utils.fs.createWriteStream(releaseBinaryFileName); downloadResponse.pipe(releaseBinaryFile.stream); } const releaseSize = downloadResponse.headers['content-length']; const bar = new progress('Downloading release [:bar] :rate/bps :percent :etas', { complete: '=', incomplete: ' ', width: 20, total: parseInt(releaseSize, 10), }); downloadResponse.on('data', (chunk) => { bar.tick(chunk.length); }) await releaseBinaryFile; if (platform === 'linux') { console.log(`Decompress ${releaseBinaryFileName}`); await utils.childProcess.exec(`tar -xvf ${releaseBinaryFileName} -C ${binPath}`, true) } else if (platformSearch === 'mingw-w64-cross') { console.log(`Install ${releaseBinaryFileName}`); const winCommand = `cmd.exe /v /c "set binpath=${binPath} && \"${releaseBinaryFileName}\" /S /D=!binpath! && echo !binpath!"`; await utils.childProcess.exec(winCommand, true) } console.log(`Delete ${releaseBinaryFileName} file`); await fsExtra.remove(releaseBinaryFileName) /* const jqueryVersion = `jquery@1.12.4`; console.log(`Install The specific ${jqueryVersion} we need`); await utils.childProcess.exec(`npm install ${jqueryVersion}`); */ console.log(`Install done`); } install(); test/000077500000000000000000000000001516104167300120415ustar00rootroot00000000000000test/fixtures/000077500000000000000000000000001516104167300137125ustar00rootroot00000000000000test/fixtures/footer.html000066400000000000000000000035571516104167300161100ustar00rootroot00000000000000
Page of
test/fixtures/header.html000066400000000000000000000034511516104167300160330ustar00rootroot00000000000000 test/fixtures/ngivr-html-template/000077500000000000000000000000001516104167300176125ustar00rootroot00000000000000test/fixtures/ngivr-html-template/ngivr-html-template.css000066400000000000000000000021471516104167300242300ustar00rootroot00000000000000.ngivr-button-row-left { padding-right: 5px; } .ngivr-position-fixed-top-left { position: fixed; top: 0px; left: 0px; } .ngivr-position-fixed-top-right { position: fixed; top: 0px; right: 0px; } .ngivr-position-fixed-bottom-left { position: fixed; bottom: 0px; left: 0px; } .ngivr-position-fixed-bottom-right { position: fixed; bottom: 0px; right: 0px; } /** template .template { // > 600 // <= 600 @media screen and (min-width: $screen-small) { } // <= 960 @media screen and (min-width: $screen-medium) { } // <= 1280 @media screen and (min-width: $screen-large) { } // <= 1920 @media screen and (min-width: $screen-huge) { } } */ body { font-size: 20px; } #pageHeader-first { height: 1cm; } #pageHeader { height: 1cm; } .ngivr-template-page-break { height: 1px; border-top-style: dotted; page-break-after: always; } .ngivr-html-template-error { color: white; background-color: red; padding: 5px; font-size: 15px; } ngivr-html-template-include { background-color: rgba(128, 128, 128, 0.5); } test/fixtures/ngivr-html-template/sygnus-logo.png000066400000000000000000001117041516104167300226120ustar00rootroot00000000000000PNG  IHDR^3ļ pHYs.#.#x?v OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-;iTXtXML:com.adobe.xmp Adobe Photoshop CC 2017 (Windows) 2017-05-22T14:18:03+02:00 2017-05-22T14:43:50+02:00 2017-05-22T14:43:50+02:00 image/png 3 sRGB IEC61966-2.1 xmp.iid:867dad6f-296b-5c4f-acac-5ab6fcaa8e1f xmp.did:4ac6bd16-7352-7744-8cd9-0a06ab89d82c xmp.did:4ac6bd16-7352-7744-8cd9-0a06ab89d82c created xmp.iid:4ac6bd16-7352-7744-8cd9-0a06ab89d82c 2017-05-22T14:18:03+02:00 Adobe Photoshop CC 2017 (Windows) saved xmp.iid:22b03d36-1575-7344-84f7-35013a97eadc 2017-05-22T14:32:57+02:00 Adobe Photoshop CC 2017 (Windows) / saved xmp.iid:867dad6f-296b-5c4f-acac-5ab6fcaa8e1f 2017-05-22T14:43:50+02:00 Adobe Photoshop CC 2017 (Windows) / 1 3000000/10000 3000000/10000 2 1 350 172 t cHRMz%u0`:o_FMIDATxwTTWsgf`.Ez#vc=1V1I4FbƊ-5N "Qzm}{)3yg-J̹~>7i@h6(@@ /@ %^@ K "@9B ކW/QVVCzbk``0D"Ӈt @VVVR#d(..L&#A rI5mmm K "@@ Dx /@ ^@ K "@@ Dx /@ ^@ K "@@ Dx /@ ^@ K "@@ Dx /@ ^@ K "@@ Dx /@ ^@ K "opp@QߵA4hB`ѳE(¿VxeVP@.W]$\.x<\.\.+Ќp*zߟiH$hiiA.CPB }ˈ-rTVVCJJ r hժLLL`kk gggj x^\.d2RRRo>tcƌH$"/@/a-JDB Ddd$ U4cR8|XYY{\RD)L&CLL ^|Ǐzzz  /#R ĹsPXXZ.Q*"55>>>A1c 888@,`o޼={Xa.++ӷL 2~\Q\\۷oc޽TŦPS<~֘={6v }}}x[ 3V\333066ɓѧO ;;;vP p8D ‡'VUU!66ϯSp%:t[[[CKK REEE$iؽ{7i\vO-@`í@DD.].j---xzzb022b7ɘ11rHKKÅ R,JZ@6)LBEE}:AVr98 1el߾rI,/@ L;z(JJJH/ߦB4d2kvM_LF"‡!K!%%;-Wкukk㭱/χ8P/W"A > eݻw˲T(޽;tuuֱ.W.ёu;@ |rA4222Yoɉ`ėSNx -EA"ŋ sM0cmmm;Zds@ xeuf{"6/a~*'d^B<;2(B*6u)AQlll0zZ] K Z2bccوmT@ `0TTT%-d ^BhsYfffpssa+@ Z޽{IQv؁L`kfmܸqZds@ XeX K.E~~~Or077҄A!M).ˑ+V͛7H$ dt/drx?XT~_B}ְIjH⅗&@cRIΞ=^D"EQ~+:::ȗC[[ב(//݋ׯ_/SG]0! 0{l7&@ hx޽Z,ED?f̘7nugS   6 #FW @h(j3m߾=]VW@yy9֮] kkk,\... [$cΰBHf Dx-^E]vjo$#Ѿ}{@ k3Bp8 Bx\.Zjccc} ///ś Bam"##IIIFQQR)Dhݺ5akk w^x5k1JJJW^!##UUU)kkk ;;;@WWlڴ ?~|ˡPlK|TUU8u;)޷o_xyyp8jqA4cccܹspuuذa`hh(5MӨd'I&[n FXX ==zo0* fBǎ֗ BHHpL|rhkk߿? :%lll~:H@P޽;qm۶b1J C*B"`ܹòeh!X HԠ͊ A& >[ne >L( 888om}>|g"((XbWٳ'F77mdh߾=zcǎ5 8>:uJS۷/fϞ P(> *";;[Q(Bee%(~5Xxq%j$ׇGgΜٳgS̳d6W mmmv? 77BHH[nakk HIIږ?=dҫMƍh߾ZIii)._ !H !jiEQD())_|+++\pBr.#rfff b\t}eee&z{d2t޽իW֭[Jca&$''ܹsر#FO?JXx1RVYYY6oތ۷o7UkX[[CKKlKC-^<\r%Zܹs''',4Myyyp=ܿ_e°a $''k׮z;'ǔ<|N³gXHQ`mm;wOj#55ׯ_ի!HXw3n&O m۶s͛FYYY\_}}222ﭭu W-BEE{]6m...055 99y&{T ---t yyyHLLMX`.]~4HNN̙39ax򂥥%b6׆Yce۶mصk+>ROn}3f tVZaÆ >|tR\~}%h1g5Kp18q}f0ӯVVV @ u9˗9R)v(Γ'OfJP`֬YX~}3V˗ixzzb>;v,P(000_VGeɗi`ժU󳉉8}4?Bcw}e˖OZ44Y ńB!|>"""}μ\8wlmm!! eWGG o)'|sssjY VZ!P^^[nK4p|jOHH<==orXjmۆv5* Æ '|yFIt8)S@GGҥ ڷo.U=ÄG wEzz:@ ȑ# N֭3ǫt]b8qB-ϟ?G||<,YҨIݻ+y---q 4蝟744D1qD#::VwgϞS>@QF޾jZQuaԩ~:JKKeksm SLaWuC5 dDW^ZL4 J/^|gaһwo\~ʀi6ݱ~v7mf:tHc_~8::ȨQ8p NP[n2uƎ.C6/Y딦iϯ.9ر6lhrj(Z0007{}P( QUUuag7H>6l@6mTl8Y?& cp\ԩS6Sjqq_iñiӦZǟ6VZǏkefffM*mNk׮Eyy0c ]I'g5f1bkmm)ߧ1988EEE۷YV.?F[n/?={ڗ.]¢E(Oܹsj$ǏNiBaw>c'yU.L &5A~~>@t6 kݗ+Cn.fLjժvZZN>4bڴiMnی3t |>uf-SREaժU믑nd׃K!3E!""wQ}7ŏQ999ajnBBMƧqFLJx<^~땕Q_k;ikkMqU%S}U4Btܹp`o߾j3֯_Xܨ* 'ЩS'l޼I9!( bڴi{.l /pppPB!M$m?vRڹsV3gά14^zaʔ)ҟ;wƔ)S@thc^GFFQحl%&4N 4j\*,?^jո0[]tx0IpV\C}w]kT\.… v~f͚.]+V@\\R?3V-[?.]&Djhd]KB2 ؽ{F u-zzzHJJjp_02 %sQ]= 3:PP@ C8pA_'wYXjM.]_kpƍ&g߾}O8ʕ+(9rDeĉz0i$ǫe5 ˗/goL&ٳ@UCzSVV4n)BJJ z!C4x@5wg0>֭[cժUXd;JzHNNQTTbŗbǎ*6EQڵk}~}Nnnnj;wMDEEv ҟNNN066V 3fқ?>$.U$ 1`ڗK|?zzz+ X2|w(deealzSk++*zB4N,gϞ8|0̙Z֭[D† T.y<aÆz_qP޽{cĉjk+VmˏikZhݺ5&lƏ~ j?W̼ߥ5k֭[ݷnoaou9fll'رc9r{=&m׮]jV,Nػw/^|<|ϟg]+۷oW _e AN"0h lݺUf# j cEA*bΝ3f =z{4(RQeVVVq1vsRGbb{}ٴijlq8d2̛7d2xyyE5,<~_V9yq8%>}: ߌL09#5y.7c9k|řqX[[vvvXd |}}ѫWf@.sشiʃ\.ϟ?k둗Bm۪O>˅l۶ {V"&QΤ mx<rXdJK*::W{ۢj^5JKرc044T)\.=R[ӧN>VZ՚/?Ap/yQl'p6mÇ7kÇ[tرCexfU .ӧk$9Mrrr KbLԩv OOuqrJk"C ۷1|Z4EQr|2nܸ!CnT4 /s688#;Ju+B(m۶ظq#qF]iFZZQ1L4 Oq&)b L22 k֬\.vڥ(--UYYib!Э[7׈as'uvN:5)x]Ǿ}T.{r9OGuD9Հ^ ]^AGG4Mӱdɒ&2♗ בyuH$BNo>,_jB@ee%Tv}gvɱ˖-?H[W5kKT c#447~'B1{K.Ezz:.\={o߾{VFUkCee%>$ԭWs_>}@&Pc4N.?İap+^6 D1b,` a``aÆח]Ssrr>mݺ5vءs\9rNÁFOM1i%k3DR熇XhfϞ&vW5z쉅 6k]vF$ YfT31ejժ^șq ,@qq޷_ 5)VuXx1֯_yyyٳJapvZFhkkTC"%_js\lZZZX,OL\;wԚ70gӧOwrW^<++K&abx}mmm/jPضm[ ɓ5U=/4^%:U3HCƁXIjƍ'JLMMqF\pM\3{5jTR|jT )•+W \֔3G~q}?$ᵷŋU|>ƌ6@,RZZZ.LMMUcYJ(|RVtݻwӧ_~]ś7o0jԨz:Ը޻wY JǻwnF i׮JkfaiiYkTD"Qkn`iiYïDh[C1~x<3nܸF&69Hi Νիa``PgT1kj{^B6Ax<)7Ɓ$JKKk檭ծ]:>I|IYYYj̮uKx[lA׮]kmQ_tKIz_τ(xyyݻ5kK|o޼or\?~U\"2 ߿? RԩSGiF\\\XeԂBÉ'`bb=|>5rommZwW|b8q#^'5r111HIIa70އ4 }}}|7􌍍pZLAA(L:񐞞ެkܒ000o={pqb1|qޜ(eee#V|ٳgGxx8 /3Й _+uvvX,nP;(jh ךIH}EQoѵkW߿_D,;W!*HgöS eO>!^p]t5!UEE߿rvxV/5 D"QZ~HL4_y.Eڵkd%ѣGc*ǜ.RSSL'''M@ c+4Ri |QQ4ɓ*#`}߿235kP\\^ Hx<6%W(1b84ÇҲbjo#F!rBr=r?b{M4 @EEEӴj rQQQjoX,ƁTƠs\dee e5e_hqC& @m|/^VL&#i`߾}L&p\[m<[[! PM @@РwE45>WWW۫4&ޥp( vBrr{u94OE4~'), gܸqڵ---TT+633S0I٦MBQ%:''l%6m4Z j,C 1*wN* |>=ٺ9.\zO ...xUׯ\.Wٗ B#%-`޽simm]#4(d}y*++A4/ô9..iTB;;#?28; %%%גۛ-=Nt k֬Qy,"..Nm޵V+V21<^6/ȑ#qQ5Z2 GH;U)( q^F|_|u5r\d2$&&"ʄt<O#-W]ߧd޼yl[U5ܹs1qzh,:tO>ERRƾߵkc4 ?xۚ FFFӧO^f /|˅-..޽{i27mĦLP}eXR\ɩƳ( UUU7nRRR>~.))QJyf <OcXd |wp8dظqFyA. Ǐoisee%[wUzh̙3yVndN͝;iiil\uZJܹsIIIrlذmڴQ(--U)r\z@ s`mmr⫨_|*-,,dYR]?~[9;;cԩ5%&&b(++BH$/O?vBVVWAT\L2>Dyy98x<^Cc(ǃD"ALL n|9ݻwg] r$CNNJ{mkժ_={8NLQ222憻w]l\0HYYFFOOlڵpqqQ?-?? $ځ`nnB4N8e˖5Rpp06mڤ$>>>:"jKRp\Xn؟R>ZNϞ=)|>}ܹs\zXxxxǏ{b1Ο?y(pPZZiӦ[-y޽2&YJ1:.:w p88z(LMMėIQ9o|8߿ߨbڴil[~?~kR-E  OOO"--=2񠥥>Ǻ$<|>ZZZ0;;… 2r8p .] HVx"=zTkD;vEuŊ8z(6 f&8p{TTTTn m333ï SS=""'OdBf˸6vz-o^Ν;+eNߏAa*SNa?~<_^ai>h7_~%땛:!!K.ŬY HrvwXti'i2;;^3GNN+v6k:| 8cǎD"kW^T&x!?S6L)ٳgcĉQΝ;044d oj ٘G@YVKYO<cǎEΝY@ x/\$ 9?J,JP(`nnwwwtppp;n3ݻwXgڵ1qz%bL&EZZ.][֨G41g 0ƐH$XéSΝ;/{n^ ˖-ٳgYmK.XHIIAtt4R)yB;bÆ ֭Zڻ}vlݺrzEll, {d2P[[[t]v9|>R)̌r/4𖔔x  f***>c}Ǐ#<</^@RR$ü\.fڵ3zݻ7ʥgΜA^^] ؉뫔 5\"##qmjY› ///Vx\.k.nZi)P( PZZd={!!!M!2#{cC509OĂH$EQ011 LMMk tttlgLdCQD" `hh>[xYo7@FFbccQRRrBNNNTK4b2>լU,pﲄeD,L:vvvJQ D#K jC-^D8q"BCCaddcҒo #:::% <<{ kT*:th۶-XR] /cr8t 022;-J0Ԧ( zzzŰ~R"++ Faa!Ô600add===D"(2@ kM6h׮kQ6CA. mڴQ^㾨ʴ%* ^u?ncŮ3eՖϖF#-\ciaVZUU;B9H$5jfyݭJlx: ͛75zgϞܹs vIR\|jmOYY|}}LgI4&WTT(ur@?]vaŊjOd2MoPטs^IQd(**¹sUhBí[ѣG :=z@ii)n߾ ǣW^5jW7HZ >رc1tP͛7X|9._ ccj ƍ_~jk׭[oCvZ@6#\]]1|p'Op<=z4FUT?.^!Cرcw}  ! P\\ CCCt4Mc޼yj}ϟ?ի\ڵ [nO?Ԩ̄zqlٲ8ulll%4###7zBPPbcc1o}Ï?Xp8aԩ ATTO$6x"^z/SL!YYYXh>sL>߇.!JӨ WWWСCؿ?v sss^j;v,47kϭ>' Dew"-C wS&LW6Ԍ&'!C Tի M{f#iIVtzAԩ]vJ[r%LLLO?Te3g@.k׮,[y<1z2UVnݪ(r~~>d2/===DFFbӦMݻw 5~7cƌF|Vg+{`-Ʀ^իWv^x xyy ?~:f̘T={GErr2d2f͚ZV 7oDPPD"`oo~՚666III(((>3w8<^z]]]̙3GQ׋/P(ׯbi.]K)1ٴTPgVo^^nܸHQFd2mk֬#GQkiXӧdؾ};ѣG$&&bp8%[[znݺӧ>}:ݫT]vd(++@ BǃJJJ`eeN:/_Daa!&OAŋHKKCAAUNyy9|||{{{K.prrBUU_\br033Cee%Zh/_D@@$ 0aoߞYY^ CCC :[lW_}ź~Znz›7om X3}:vXеkװdOF߾}J1b֬YsaӦM8t V ( >6lR? AЧO9r?C)$1sLښT_BOO k*| mۆC;v@AAA l޽ظq#ڵk777:tCUd,\,]ӧOq%ܹ# 6ʕ+˱h"Z>ĉhѢzGsrb|ر.\Gj]fRLHH`~@ee%:tj}:{俠| 222c۶mu oLL ;f "xyyA*bȑ p=ӧO1o<ŋBvv6<==1k,'#www<CeΝ ;w[l;{E :VVV0agϞ:ʢ_zE5_~~>GߺunAAA%=zhzȐ!U>Dwؑ~---N:~5=o<Ғ3gK9s>~8-yuFGtnn.M4mmmM_xWɓ'Ri-,,  ҥ v܉1c@,?èӧP^^333"DWWAUUk-[г#&&033>o DGGGʕ+pz`bbt)Y l^r;v-[9sŋؾ};>S%֭[_гgOv5ǏL&cqkkk}enj3b=zR|>***~̙3Z[0c 8::bǎ͛*?/ѽ{wPm۲mdb/xMк!CѣGxiFǎfxxx͛0aΞ=[璒Tui}% ry0P###6L&ŋA4Z|9V^~믿ƬYХK Á^Ct&4ž={@4Pĭ\}i KKKbc'l0/G&ȍ䨬DYY6mڵ r;9՚R)… 1fڵ gΜ?q)PTט2e D"[*<<\.VVVɓ'cڴiJBѷo_9ȑ#k.8q̙OOO 鉊 J@M9}v3>o 66 عs'F:rF"222`ffNR  88W^ȑ#>7uThiiJ1yyy( ƍZ0|B!9sY pM_? .`߾}011Q[///СC5V-9uTΝS+,zJeuP͵ Y+Wcǎ U^~R]jj*lll q̙+..Ƌ/9uT<K,L&?(A۵kW\pr&MRZ1E^^999˃*++͛wnܸwqqq*ٜL l薷7,X<)UZVVVk=Μ9/_ѣYA wŋSSSСC5)Jxc&UΝ*|JB'aܾ}DPP%1uTpFk >Ő!C2a0B!222jns-"-ڥ(J)X,>#}{vѣtuuUV:]~=/dq{J~PUU5kԈضmPQQ㧟~(b28D"HKKôiӔ9l0hiiaɒ%5Dyڵx  >??w g}b\ƄqF2QL'?UL`W} |PbMo?VС޼yS27RC]Ѹtu|ȑ#`>ݻw=qu1 @ tؑ a111Xx1yfeeeHLLDll,+)Sm|Ѯ];Ո`MڇX,FQQQ Ý;w`mm;w`С Ǐ-nܸ!C̙3tzKKKՋ-4]G}3g"998z(n޼'"""FaCL̦P(DUUk >/۷qF.] ((_`Ȑ!pvvFV0n8cҤI5oܸ ddd 00Ř5kx<ۇ3f G1l06AP< nuu?+SֆL&v tʕ4iѿ\pg޽ !3!((sbСMZZZضmkX?W^᫯=O?y$Ο?]]],^Æ ۷ocѢE8s [F’%K$蹹XlY χ>333(ݕ+Wp]X[[ 'ODnn.bbbaTUU!''OF^^b1hV_Ō33$, M8}t+z3&61 :::HNNP(sAAn޼˗/#&&H$ec>DHHJJJ˗/|>vvvpuuœ'O\z Wɓ(((@~~>_CEQQn޼7n 'OdwO<޽{1dxyy!22N“'O ahhmۢ W^Exx8޼yH+++Ƌ/n,:t$777طoBBB LLL=zG w܁vヤ$"GEhh([u)))ػw/333ݻ8u[ظq#RRRF{֭[ABB9p8Ahh(2z*RRRتǏDZcǠlTUU :::066ƥKpY4 &@GG?rSӧw>|/_ݻ_qI@&oAZZ>cʕ+ KpI9:uB=PTTgϞ!$$ϟGbb"9{{{<}˗/A4жm[! /_DFF#G **y1<kkkx{{ɓ'֭qEطoO>#G <qqqBvЦMݻ(**\.=wɓ'q%TVV"++ 000@كK*Wk꨹FICkɓ'D077L&CBBlllľ"]ʔ"''fffJWoFnn.ڴinݺǏCGGg-zt6rRRR޿aCw&88/^֭[+ٍ BAA>c6|'#''l蕵5ͬtڕxbccsxۇĕ^\ MӈDyy9lmmA4Ҡ]($$$ݩH$̄,--sXYYA[[999@ǎTxN^QQQ|g4X[[C&! /^`IT?'P %<Á!QQQ؟װEYYѣG.dhiii)>|L( xzz8hkk J>oH'''ֵ|1uO>(,,ӧO LDtҥFbxcҤIGjj*zn"ޖ!%"@@ Dx /@ IkMnM]]]Bwa›FX_9IvHIIAFFkiD"tرi›) {rtv@ |8TUUƦ^[ @ %"@@ Dx /@ ^@ %" I"IENDB`test/fixtures/ngivr.html000066400000000000000000000045411516104167300157310ustar00rootroot00000000000000
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test0
test1
test2
test3
test4
test5
test6
test7
test8
test9
test10
test11
tes12
test13
test14
test15
test16
test17
test/fixtures/test.html000066400000000000000000000000741516104167300155600ustar00rootroot00000000000000 this is a test test/scripts/000077500000000000000000000000001516104167300135305ustar00rootroot00000000000000test/scripts/ngivr.sh000077500000000000000000000004551516104167300152200ustar00rootroot00000000000000#!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $DIR/vars.sh rm $BUILD_PDF/ngivr.pdf || true $BIN --title NGIVR --orientation Landscape --page-size A4 $FIXTURES/ngivr.html --header-html $FIXTURES/header.html --footer-html $FIXTURES/footer.html $BUILD_PDF/ngivr.pdftest/scripts/test.sh000077500000000000000000000002551516104167300150500ustar00rootroot00000000000000#!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $DIR/vars.sh rm $BUILD_PDF/test.pdf || true $BIN $FIXTURES/test.html $BUILD_PDF/test.pdftest/scripts/vars.sh000077500000000000000000000004461516104167300150460ustar00rootroot00000000000000#!/usr/bin/env bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" ROOT=$DIR/../.. BIN=$ROOT/release/wkhtmltox/bin/wkhtmltopdf BUILD=$ROOT/build FIXTURES=$ROOT/test/fixtures BUILD_PDF=$BUILD/pdf mkdir -p $BUILD_PDF export ROOT export BIN export BUILD export FIXTURES export BUILD_PDF