我有以下html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Registration Form</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="./registration.css">
</head>
<body>
<header>
<nav class="navbar navbar-expand-md navbar-dark bg-primary">
<a class="navbar-brand" href="/">
<img src="/favicon-32x32.png">
<span class="d-none d-md-inline">Member Management Cypriot FOSS Community</span>
<span class="d-md-none">Ellakcy</span>
</a>
</nav>
</header>
<div class="container-fluid">
<h1 class="text-center">Registration Form</h1>
<!-- 1rst step -->
<div class="row" data-comment="step-1">
<div class="col ml-3 mr-2 p-2 shade border float-left">
<div class="row">
<div class="col-12">
<h2 class="text-center">Step 1: Fill with the required details</h2>
</div>
</div>
<div class="row">
<form id="registrationForm" data-scroll-to="displayRegistationPaperApplicationWrapper" class="col m-1">
<div class="row">
<div class="form-group col-12 col-md-12 col-lg-6">
<label for="inputName">Name <i class="fas fa-database"></i> *</label>
<input class="form-control" data-qr="true" type="text" data-on-reset="no-val" name="name" placeholder="" required>
</div>
<div class="form-group col-12 col-md-12 col-lg-6">
<label for="inputName">Surname <i class="fas fa-database"></i>*</label>
<input class="form-control" data-qr="true" data-on-reset="no-val" type="text" name="surname" required>
</div>
</div>
<div class="row">
<div class="form-group col">
<label for="inputName">Contact Email <i class="fas fa-database"></i> <i class="fas fa-envelope"></i> *</label>
<input id="registrationEmail" data-autofill="notification-email" class="form-control" data-qr="true" data-on-reset="no-val" type="email" name="email" required>
</div>
</div>
<div id="signatureContainer" class="row">
<input id="selectSignature" type="file" data-on-reset="no-val" style="display:none" name="signature" />
<div class="droparea"><label id="replaceWithImage" for="selectSignature"><span>(Optionally)<br>Click or Drop<br>To select your signature image</span></label></div>
</div>
<div class="row mt-3">
<div class="col">
<button id="step1" type="submit" class="btn btn-block btn-primary"> Next Step </button>
</div>
</div>
</form>
</div>
</div>
<div class="col mr-3 ml-3 border shade float-right">
<hr class="d-md-none d-lg-none d-block">
<h2 class="col-12 text-sm">Note:</h2>
<ul class="col-12" style="list-style:none">
<li class="mb-1">* The fields are mandatory to get filled</li>
<li class="mb-1"><i class="fas fa-database"></i> The field contain information that will stored to the database <b>when the application has been approved by the council</b>. </li>
<li class="mb-1"><i class="fas fa-envelope"></i> The field contains information used in order to get contact with you regarding the registration procedure. </li>
</ul>
</div>
</div>
<!-- last step -->
<div id="displayRegistationPaperApplicationWrapper" data-step="2" style="display:none" class="row border shade mt-5">
<div class="col-12">
<h2 class="text-center">Step 3: Print the following form</h2>
<button class="btn btn-primary mb-2 mt-1" onclick="printPaperApplciationForm()"><i class="fas fa-print"></i></button>
<button id="downloadPdf" class="btn btn-danger mb-2 mt-1" onclick="getPDf()"><i class="far fa-file-pdf"></i></button>
</div>
<iframe id="displayRegistationPaperApplication" name="displayRegistationPaperApplication" class="col-12"></iframe>
</div>
<!-- templates section -->
<!-- template for the printable application for member registration -->
<script id="registationPaperApplication" type="text/x-jsrender">
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Member Registration Form</title>
<style>
#printArea {
width:100%;
}
table{
width:100%;
}
#titleWrapper{
text-align:center;
}
.signature {
text-align: center;
}
.signature h4{
font-size: 0.8rem;
text-align:center;
font-style:oblique;
border-bottom: 1px solid;
}
.signature * {
max-width:100%;
text-align: center;
}
.signature * img {
max-width:100%;
width: 300px;
height: 300px;
}
#scanQr {
text-align:center;
max-width:100%;
}
#scanQr * {
max-width:100%;
width: 300px;
height: 300px;
}
.applicationData{
text-align: center;
}
.banner-wrap{
padding:auto;
text-align:center;
}
.banner {
margin-left: auto;
margin-right: auto;
}
#memberInfo{
font-size:20px;
}
</style>
</head>
<body>
<div class="banner-wrap">
<img class="banner" src="/ellakcy-full.png" />
</div>
<div id="titleWrapper">
<h1>Member Registration Form</h1>
</div>
<table id="memberInfo" border="1">
<!-- I use table with multiple columns in order to have the data sorted for the print media as well -->
<colgroup>
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
</colgroup>
<thead>
<th colspan="20"><h2 class="text-center">Member Personal Information</h2></th>
</thead>
<tbody>
<tr><!-- Name & surname -->
<th colspan="2" >Name:</th>
<td colspan="8" class="applicationData"><span class="registrationInfo" data-fill="name">{{:name}}</span></td>
<th colspan="2">Surname:</th>
<td colspan="8" class="applicationData" ><span class="registrationInfo" data-fill="surname">{{:surname}}</span></td>
</tr>
<tr><!-- email -->
<th colspan="3">Contact Email:</th>
<td colspan="17" class="applicationData" colspan="3"><span class="registrationInfo" data-fill="email">{{:email}}</span></td>
</tr>
</tbody>
</table>
<table id="signatureArea">
<colgroup>
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
</colgroup>
<tr>
<td class="signature" colspan="6">
<h4>Signature of President:</h4>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAQAAADTdEb+AAACHElEQVR42u3SMQ0AAAzDsJU/6aGo+tgQouSgIBJgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC4yFsTAWGAtjYSwwFsbCWGAsjIWxwFgYC2OBsTAWxgJjYSyMBcbCWBgLjIWxMBYYC2NhLDAWxsJYYCyMhbHAWBgLY4GxMBbGAmNhLIwFxsJYGAuMhbEwFhgLY2EsMBbGwlhgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC2OBsTAWxgJjYSyMBcbCWBgLjIWxMBYYC2NhLDAWxsJYYCyMhbHAWBgLY4GxMBbGAmNhLIwFxsJYGAuMhbEwFhgLY2EsMBbGwlhgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC4yFsTAWGAtjYSwwFsbCWGAsjIWxwFgYC2OBsTAWxgJjYSyMBcbCWBgLjIWxMBbGAmNhLIwFxsJYGAuMhbEwFhgLY2EsMBbGwlhgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC4yFsTAWGAtjYSwwFsbCWGAsjIWxwFgYC2OBsTAWxgJjYSyMBcbCWBgLjIWxMBYYC2NhLDAWxsJYYCyMhbHAWBgLY4GxMBbGAmNhLIwFxsJYGAuMhbEwFhgLY2EsjCUBxsJYGAuMhbEwFhgLY2EsMBbGwlhgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC4yFsTAWGAtjYSwwFsbCWGAsjIWxwFjsPeVaAS0/Qs6MAAAAAElFTkSuQmCC">
</td>
<td id="scanQrContainer" colspan="8">
<div id="scanQr">{{if qrCodeImg}}
<img src="{{:qrCodeImg}} "/>
{{/if}}
</div>
</td>
<td class="signature" colspan="6">
<h4>Member Signature:</h4>
<div class="user-signature" data-fill="signature"> {{if signature }}
<img src="{{:signature }}" />
{{/if}}
</div>
</td>
</tr>
</table>
</body>
</html>
</script>
</div>
<script src="https://code.jquery.com/jquery-3.4.0.min.js" integrity="sha256-BJeo0qm959uMBGb65z40ejJYGSgR7REI4+CW1fNKwOg=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.debug.js" integrity="sha384-NaWTHo/8YCBYJ59830LTz/P4aQZK1sS0SneOgAvhsIl3zBu8r9RevNg5lHCHAuQ/" crossorigin="anonymous"></script>
<script src="https://unpkg.com/jspdf-autotable@3.1.1/dist/jspdf.plugin.autotable.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsrender/1.0.2/jsrender.min.js"></script>
<script src="./animatescroll.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-cookie@2.2.0/src/js.cookie.min.js"></script>
<script src="./register.js"></script>
</body>
</html>
它使用了以下javascript(我知道它有点密集):
/**
* A node in the DOM tree.
*
* @external Node
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node Node}
*/
// Defining the appropriate reset actions
var allowedResetVals=["set-class","set-val","no-val","remove"];
/**
* Sets a hidden Input to the form with a specific name and value
* @param {String} name The name of the inputElement
* @param {String} value The value of the inputElement
* @param {String} onResetAction What to do when reset has been triggered
* @param {String} onResetValue Depending the cation what value to set
*/
var appendHiddenInput=function(name,value,onResetAction,onResetValue){
var input=null;
if($("input[name="+name+"]").length>0) {
input="input[name="+name+"]";
$(input).val(value);
} else {
input= document.createElement("input");
input.type='hidden';
input.name=name;
input.value=value;
$("#registrationForm").append(input);
}
if(onResetAction){
setRetetAttributes(input,onResetAction,onResetValue);
}
}
/**
* Bootsrtaping the attributes whyen reset is occured
* @param {Node | String} element The element to get bootstrapped with parameters (required)
* @param {String} onResetAction The action that is needed to be executed when reset is occured (required)
* @param {String} onResetValue Depending the action it may need to provide some value
*
* @throws {Error} In case when an invalid action has been provided or an action that needs a value does not have one
*/
var setRetetAttributes=function(element,onResetAction,onResetValue){
onResetAction=onResetAction.toLowerCase();
if($.inArray(onResetAction,allowedResetVals)!==-1) {
$(element).attr('data-on-reset',onResetAction);
if(onResetAction==='set-class' || onResetAction==='set-val'){
if(!onResetValue) {
throw Error('The action '+onResetAction+" requires a value to get provided");
}
$(element).attr('data-on-reset-value',onResetValue);
}
} else {
throw Error("The data-on-reset value should be either one of the following values: "+allowedResetVals.toString()+" but you provided the value (converted in lower case): "+onResetAction);
}
}
/**
* Function that reads an image as base64 content.
* @param {Array} files The list of the files
* @param {Function} cb Callback function where the base64 content will get processed
*/
var encodeImageFileAsURL = function(files,cb) {
var file = files[0];
var reader = new FileReader();
reader.onloadend = function () {
if(reader.error){
console.error(reader.error.message);
} else {
cb(reader.result);
}
}
reader.readAsDataURL(file);
}
/**
* Retrieve image as Base64 and set it into the approriate elements
* @param {String} base64Img The image contents as base64
*/
var setImageValues=function(base64Img){
$('#replaceWithImage span').css("display","none");
$('#replaceWithImage img').remove();
$('#replaceWithImage').append("<img src=\""+base64Img+"\"/>");
appendHiddenInput('imgBase64',base64Img,'remove');
}
/**
* Convert a value into a boolean
* @param {Mixed} value The value to check convert into boolean
* @return {Boolean}
*/
var boolVal=function(value){
var falseValues=['false',0,undefined,'0','no','null',null];
if (typeof value === 'string' || value instanceof String){
value=value.toLowerCase();
}
return $.inArray(value, falseValues) === -1
}
/**
* Stripping html Content
* @param {String} string An html dirty string
* @return {String} without any html content
*/
var stripHtml=function(string){
var div = document.createElement("div");
div.innerHTML = string;
return div.innerText;
}
/**
* Change the dom to the next step
* @param {external:Node | String} currentElement The element shown to the current step
* @param {Function} callback The callback when moved to the next step
*/
var nextStep = function(currentElement, callback) {
var idToScrollTo=$(currentElement).attr('data-scroll-to');
$("#"+idToScrollTo).removeClass('d-none');
$("#"+idToScrollTo).show();
$("#"+idToScrollTo).animatescroll({scrollSpeed:2000,
easing:'easeInQuad',
onScrollEnd:function(){
if(callback){
callback();
}
}
});
}
/**
* Write content to an Iframe
* @param {String} id The id of the iframe
* @param {String} url The content to write into the iframe
*/
var writeContentToIframe=function(id,url){
var iframeElementContainer = document.getElementById(id);
iframeElementContainer.src=url;
}
/**
* Autofills an input or a element from the value provided from an input
* @param {external:Node | String} element The button element where the info and the
*/
var autofill=function(element){
var autoFillId=$(element).attr('data-autofill');
var valueToCopy=$(element).val();
$('input[data-autofill="'+autoFillId+'"]').val(valueToCopy);
$('span[data-autofill="'+autoFillId+'"]').text(valueToCopy);
}
/**
* @param {Function} cb The callback then the captha Image has been loaded.
*/
var resetCaptha=function(cb)
{
var url=$('meta[name=captha_url]').attr("content")+"?rand="+Math.random();
if(cb){ //Call a callback function when image has been loaded
$("#capthaImage").on('load',function(e){
cb();
});
}
$("#capthaImage").attr('src',url);
}
/**
* Generate and save pdf as Html
* @param {jsPDF} pdf The PDF Creation object
* @param {String} html The html content
* @param {function} callback callback function for further processing
* @returns {jsPDF}
*/
var generatePdfFromHtml=function(pdf,html,callback){
console.log("Generating Pdf");
pdf.html(html, {
'callback': function(pdf){
if(callback && typeof callback === 'function'){
console.log("Offering Pdf as callback");
callback(pdf);
}
}
});
}
$(document).ready(function(){
/**
* @var {jsPDF} pdf
*/
var pdf=new jsPDF('p', 'pt', 'a4');
$('#selectSignature').on("change",function(e){
e.preventDefault()
encodeImageFileAsURL(e.target.files,setImageValues);
});
// Handle Signature img read
$('#signatureContainer').on('drop',function(e){
if(e.originalEvent.dataTransfer && e.originalEvent.dataTransfer.files.length){
e.preventDefault();
e.stopPropagation();
encodeImageFileAsURL(e.originalEvent.dataTransfer.files,setImageValues);
}
$(this).removeClass('dragging');
})
// Signature Image Drag'n'Drop
$("#signatureContainer").on("dragover", function(event) {
event.preventDefault();
event.stopPropagation();
$(this).addClass('dragging');
});
$("#signatureContainer").on("dragleave", function(event) {
event.preventDefault();
event.stopPropagation();
$(this).removeClass('dragging');
});
//On Registration Form Submit
$('#registrationForm').on("submit",function(e){
e.preventDefault();
var self=this
var values=$(this).serializeArray();
var qrValueInputNames=$('input[data-qr="true"]').map(function(){return this.name;}).get();
var qrCodeValues={};
var valuesToRender={};
//Setting the values
$.each(values,function(index,item){
// In order to prevent XSS we convert the values into their plaintext form
item.value=stripHtml(item.value);
if(item.name==='imgBase64'){
valuesToRender['signature']=item.value;
} else {
valuesToRender[item.name]=item.value;
}
if($.inArray(item.name, qrValueInputNames) !== -1){
qrCodeValues[item.name]=item.value;
}
});
//Setting the QRcode to get scanned during registration
var qrious=new QRious({ size: 200, value: JSON.stringify(qrCodeValues)})
valuesToRender.qrCodeImg=qrious.toDataURL();
var tmpl = $.templates('#registationPaperApplication');
var html= tmpl.render(valuesToRender);
//@var {jsPDF} pdf
generatePdfFromHtml(pdf,html,function(pdfFromCallback){
var blob=pdfFromCallback.output('blob');
var blob_url = URL.createObjectURL(blob);
pdfFromCallback.save('application_form.pdf');
writeContentToIframe('displayRegistationPaperApplication',blob_url);
nextStep(self);
});
$('#registrationForm').trigger('clear');
});
});
代码使用以下html jsrender
模板生成Pdf:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Member Registration Form</title>
<style>
#printArea {
width:100%;
}
table{
width:100%;
}
#titleWrapper{
text-align:center;
}
.signature {
text-align: center;
}
.signature h4{
font-size: 0.8rem;
text-align:center;
font-style:oblique;
border-bottom: 1px solid;
}
.signature * {
max-width:100%;
text-align: center;
}
.signature * img {
max-width:100%;
width: 300px;
height: 300px;
}
#scanQr {
text-align:center;
max-width:100%;
}
#scanQr * {
max-width:100%;
width: 300px;
height: 300px;
}
.applicationData{
text-align: center;
}
.banner-wrap{
padding:auto;
text-align:center;
}
.banner {
margin-left: auto;
margin-right: auto;
}
#memberInfo{
font-size:20px;
}
</style>
</head>
<body>
<div class="banner-wrap">
<img class="banner" src="/ellakcy-full.png" />
</div>
<div id="titleWrapper">
<h1>Member Registration Form</h1>
</div>
<table id="memberInfo" border="1">
<!-- I use table with multiple columns in order to have the data sorted for the print media as well -->
<colgroup>
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
</colgroup>
<thead>
<th colspan="20"><h2 class="text-center">Member Personal Information</h2></th>
</thead>
<tbody>
<tr><!-- Name & surname -->
<th colspan="2" >Name:</th>
<td colspan="8" class="applicationData"><span class="registrationInfo" data-fill="name">{{:name}}</span></td>
<th colspan="2">Surname:</th>
<td colspan="8" class="applicationData" ><span class="registrationInfo" data-fill="surname">{{:surname}}</span></td>
</tr>
<tr><!-- email -->
<th colspan="3">Contact Email:</th>
<td colspan="17" class="applicationData" colspan="3"><span class="registrationInfo" data-fill="email">{{:email}}</span></td>
</tr>
</tbody>
</table>
<table id="signatureArea">
<colgroup>
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
<col width="5%"><col width="5%">
</colgroup>
<tr>
<td class="signature" colspan="6">
<h4>Signature of President:</h4>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAQAAADTdEb+AAACHElEQVR42u3SMQ0AAAzDsJU/6aGo+tgQouSgIBJgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC4yFsTAWGAtjYSwwFsbCWGAsjIWxwFgYC2OBsTAWxgJjYSyMBcbCWBgLjIWxMBYYC2NhLDAWxsJYYCyMhbHAWBgLY4GxMBbGAmNhLIwFxsJYGAuMhbEwFhgLY2EsMBbGwlhgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC2OBsTAWxgJjYSyMBcbCWBgLjIWxMBYYC2NhLDAWxsJYYCyMhbHAWBgLY4GxMBbGAmNhLIwFxsJYGAuMhbEwFhgLY2EsMBbGwlhgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC4yFsTAWGAtjYSwwFsbCWGAsjIWxwFgYC2OBsTAWxgJjYSyMBcbCWBgLjIWxMBbGAmNhLIwFxsJYGAuMhbEwFhgLY2EsMBbGwlhgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC4yFsTAWGAtjYSwwFsbCWGAsjIWxwFgYC2OBsTAWxgJjYSyMBcbCWBgLjIWxMBYYC2NhLDAWxsJYYCyMhbHAWBgLY4GxMBbGAmNhLIwFxsJYGAuMhbEwFhgLY2EsjCUBxsJYGAuMhbEwFhgLY2EsMBbGwlhgLIyFscBYGAtjgbEwFsYCY2EsjAXGwlgYC4yFsTAWGAtjYSwwFsbCWGAsjIWxwFjsPeVaAS0/Qs6MAAAAAElFTkSuQmCC">
</td>
<td id="scanQrContainer" colspan="8">
<div id="scanQr">{{if qrCodeImg}}
<img src="{{:qrCodeImg}} "/>
{{/if}}
</div>
</td>
<td class="signature" colspan="6">
<h4>Member Signature:</h4>
<div class="user-signature" data-fill="signature"> {{if signature }}
<img src="{{:signature }}" />
{{/if}}
</div>
</td>
</tr>
</table>
</body>
</html>
但是,就像在浏览器中一样,pdf占据了整个填充区的宽度,就像在sample rendering中所看到的那样,它紧贴左侧。
您知道我如何使jspdf使用全角pdf呈现或指定的花药标记从给定模板生成pdf吗?