使用jspdf从Jsrender模板生成的PDF错误

时间:2019-04-21 16:03:04

标签: jspdf jsrender

我有以下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>&nbsp;<i class="fas fa-envelope"></i>&nbsp;*</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吗?

0 个答案:

没有答案