3

I use my custom Persian font in jsPDF, but characters for this font show me wrong.

I hear

The IranNastaliq font uses the 'cswh' feature for such ligatures. 'cswh' is default-off according to the OpenType spec. The font should use 'calt' instead. It's unfortunate

So should I set 'cswh' in the jsPDF library? How can I do that in jsPDF code?

https://evenz.ir/pdf/js/

var doc = new jsPDF();
doc.addFont("test/reference/fonts/IranNastaliq-Web.ttf", "IranNastaliq", "normal");

doc.setFont("IranNastaliq"); // Set font
doc.setFontSize(150);

var persianText = "نگار شکار";

doc.text(persianText, 10, 60);

Enter image description here

3
  • 1
    1 - I don’t know if it’s possible with jsPDF or not, 2 - but I can suggest another solution, you can create an image of the word : img.src = 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="' + canvas.width + '" height="' + canvas.height + '">' + parseandserialize('نگار') + '</foreignObject></svg>'); Commented 2 days ago
  • In addition, IranNastaliq is a Persian font. Commented 2 days ago
  • thanks but i need to use jsPDF to generate pdf for my software, svg will not help . Commented 2 days ago

1 Answer 1

0

2025: jspdf doesn't support Open Type features to this date

This is also reported in the github issue section: "Custom font does not get rendered properly in the PDF".

Workaround: try pdfkit.js

Admittedly, unsatisfying to switch the library but perhaps the easiest solution.

pdfkit support Open Type feature settings in text renderings.
You can add them as an object to the text properties like so:


  // write text
  const text = "نگار شکار";
  const [x, y] = [10, 60];

  // add your content to the document here, as usual
  doc.fontSize(100);

  // define opentype feaures
  const features = {
    features: {
      liga: true,
      cswh: true
    }
  };

  doc.text(text, x, y, features);  

Working example

/**
 * init pdfkit
 * and create blob for rendering
 */

(async () => {
  const doc = new PDFDocument();

  // pipe the document to a blob
  const stream = doc.pipe(blobStream());

  // load custom font and register
  let fontUrl =
    "https://cdn.jsdelivr.net/npm/@raha.group/[email protected]/IranNastaliq.regular.ttf";


  const font = await fetch(fontUrl);
  const buffer = await font.arrayBuffer();

  doc.registerFont("CustomFont", buffer);
  doc.font("CustomFont");

  // write text
  const text = "نگار شکار";
  const [x, y] = [10, 60];

  // add your content to the document here, as usual
  doc.fontSize(100);

  // define opentype feaures
  const features = {
    features: {
      liga: true,
      cswh: true
    }
  };

  doc.text(text, x, y, features);

  // get a blob when you're done
  doc.end();

  // get objectURL for display or download
  stream.on("finish", async function() {
    let blob = stream.toBlob("application/pdf");
    let objectURL = await URL.createObjectURL(blob);

    // set download link
    btnDownload.href = objectURL

    // render via pdf.js
    renderPDF(objectURL);
  });
})();
<!-- pdfkit helper -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/js/pdfkit.standalone.min.js"></script>
<!-- blob helper -->
<script src="https://github.com/devongovett/blob-stream/releases/download/v0.1.3/blob-stream.js"></script>

<!-- pdf.js: just for displaying PDF -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>


<style>
  canvas {
    border: 1px solid #000
  }
</style>


<p><a id="btnDownload" href="" download="file.pdf">Download</a></p>
<canvas id="pdfCanvas" style="border:1px solid #ccc; width:100%;"></canvas>

<script>
  // render pdf blob to pdf.js viewer instance
  async function renderPDF(url) {
    const canvas = document.getElementById("pdfCanvas");
    const ctx = canvas.getContext("2d");

    // Load PDF
    const pdf = await pdfjsLib.getDocument(url).promise;

    // Get first page
    const page = await pdf.getPage(1);

    // Prepare viewport
    const viewport = page.getViewport({
      scale: 1.5
    });
    canvas.width = viewport.width;
    canvas.height = viewport.height;

    // Render
    await page.render({
      canvasContext: ctx,
      viewport
    }).promise;
  }
</script>

In the above example I'm rendering the pdf output via pdf.js merely for the sake of illustration (we can't render PDFs in iframes/snippets) – you don't need this for the PDF creating itself.

Emulating substitution features?

Sorry not possible. The main problem: OT Feature substitutions replace certain glyphs (so only the text representation) while the text/string data is unchanged.

To apply substitutions the text shaping engine must support Open Type features. The glyph swapping happens via internal glyph ID mapping – we can't access these in a browser e.g via JS without a full blown font parsing or shaping library.

So there is no way to change the input string to show alternative glyphs as these alternate designs can't be accessed by a unicode point.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.