title: Is there a way to convert an SVG element to a PDF while maintaining its CSS properties?
body: <p>I have a svg graph that is generated using the d3 library and styled using css. I would like to convert this graph to a PDF while maintaining its CSS properties. </p>
  
  <p>I have tried various different approaches but none seem to capture the svg with its style attributes. Currently, I'm using html2canvas and jsPDF for creating a canvas and saving it as a PDF. 
  I have included the code for a minimal reproducible example of the styled graph + the code I currently have to generate a PDF. </p>
  
  <pre><code>&lt;script src="https://d3js.org/d3.v5.js"&gt;&lt;/script&gt;
  
  &lt;!-- jQuery CDN --&gt;
  &lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"&gt;
  &lt;/script&gt;
  
  &lt;!-- html2canvas CDN --&gt;
  &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.js" type="text/javascript"&gt;
  
  &lt;!-- jsPDF CDN --&gt;
  &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.debug.js"
      integrity="sha384-NaWTHo/8YCBYJ59830LTz/P4aQZK1sS0SneOgAvhsIl3zBu8r9RevNg5lHCHAuQ/" crossorigin="anonymous"&gt;
  &lt;/script&gt;
  
  &lt;script&gt;
      function genPDF() {
          // gets the quality based on the value that is input in the field
          if (quality = document.getElementById("quality").value) {
              // window.devicePixelRatio = 2; 
              html2canvas(document.getElementById("svg1"), {
                  // scale based on quality value input
                  scale: quality,
                  // logging for debugging
                  logging: true,
                  letterRendering: 1,
                  // allows for cross origin images
                  allowTaint: true,
                  useCORS: true
              }).then(function (canvas) {
                  // creating a canvas with page data 
                  var img = canvas.toDataURL('image/png');
                  // creating a portrait a4 page
                  var doc = new jsPDF('l', 'mm', 'a4');
                  // setting the width of the page to auto
                  const imgProps = doc.getImageProperties(img);
                  const pdfWidth = doc.internal.pageSize.getWidth();
                  const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
                  // adding the image canvas to the pdf and saving
                  doc.addImage(img, 'PNG', 2, 2, pdfWidth, pdfHeight);
                  doc.save('generatedPDF.pdf');
              });
          } else {
              // throw if no scale value is entered
              throw "Please enter a scale value!";
          }
      }
  &lt;/script&gt;
  &lt;style&gt;
  
      rect {
          fill: #868e96;
          opacity: 0.3;
      }
  &lt;/style&gt;
  
  &lt;body&gt;
  &lt;form&gt;
      Enter scale quality:&lt;br&gt;
      &lt;input type="text" id="quality" name="quality" placeholder="1 - 5"&gt;
  &lt;/form&gt;
  &lt;button onclick="genPDF()"&gt;Generate PDF&lt;/button&gt;
      &lt;svg id="svg1" width="1000" height="700"&gt;
          &lt;g id="elementsContainer"&gt;
  
              &lt;rect x="25" y="25" width="240" height="240" /&gt;
              &lt;rect x="275" y="25" width="240" height="240" /&gt;
              &lt;rect x="25" y="275" width="240" height="240" /&gt;
              &lt;rect x="275" y="275" width="240" height="240" /&gt;
     &lt;/g&gt;
  &lt;/svg&gt;
  &lt;/body&gt;
  </code></pre>
  
  <p>I just get a black non-styled graph.</p>
  
  <p>Edit: 
  I tried adding a timeout function which still does not seem to solve the issue: </p>
  
  <pre><code>function genPDF() {
          // gets the quality based on the value that is input in the field
          if (quality = document.getElementById("quality").value) {
              // window.devicePixelRatio = 2; 
              html2canvas(document.getElementById("apply"), {
                  // scale based on quality value input
                  scale: quality,
                  // logging for debugging
                  logging: true,
                  letterRendering: 1,
                  // allows for cross origin images
                  allowTaint: true,
                  useCORS: true
              }).then(function (canvas) {
                  setTimeout(function () {
                      var img = canvas.toDataURL('image/png');
                      var doc = new jsPDF('l', 'mm', 'a4');
                      const imgProps = doc.getImageProperties(img);
                      const pdfWidth = doc.internal.pageSize.getWidth();
                      const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
                      doc.addImage(img, 'PNG', 2, 2, pdfWidth, pdfHeight);
                      doc.save('generatedPDF.pdf');
                  }, 3000);
              });
          } else {
              // throw if no scale value is entered
              throw "Please enter a scale value!";
          }
      }
  </code></pre>
  
  <p>Edit: The solution that I found works the best is just to simply add the style properties inline to the SVG tags. </p>