opentype.js icon indicating copy to clipboard operation
opentype.js copied to clipboard

GPOS write support

Open PascalPixel opened this issue 2 years ago • 11 comments

@felipesanches made initial headway into this, but never opened a pull request.

Is any of this usable / salvageable?

This relates to #95 - I create a pixel font from scratch using opentype.js ❤️ and would love to add kerning

PascalPixel avatar Feb 05 '23 12:02 PascalPixel

I could have sworn we already have GPOS implemented.

ILOVEPIE avatar Feb 05 '23 19:02 ILOVEPIE

We do have it implemented, but we need more formats added to it see #491

ILOVEPIE avatar Feb 05 '23 19:02 ILOVEPIE

We do have it implemented, but we need more formats added to it see #491

As far as I can see, #491 adds read support, but not write support.

It's quite unfortunate that this PR consists of so many commits and most are tagged with WIP in the commit message, which makes it very hard to see whether this is ready or not. Maybe @felipesanches can tell us more, even though its been 84 7 years?

Connum avatar Feb 05 '23 21:02 Connum

I'd say this probably needs re-implementation, I doubt we can merge this code. We definitely can use it for reference though.

ILOVEPIE avatar Feb 05 '23 21:02 ILOVEPIE

I will try to review my old code and let you know what I think. Please give me a few days to go through the content of those commits.

felipesanches avatar Feb 06 '23 15:02 felipesanches

I am now rebasing everything into a single commit and fixing the conflicts with current master branch. I also found a couple more left over commits on my local working copy that included some more refactoring. I cannot remember what I wrote 7 years ago, but I will include those as well.

felipesanches avatar Feb 06 '23 16:02 felipesanches

Thanks @felipesanches! I'm currently using fontforge, directed from node, with a very weird long command string to get the kernings in 😄

import { exec as oldExec } from "child_process";
import { promisify } from "util";

const exec = promisify(oldExec);

class FontForgeError extends Error {
  constructor(message: string) {
    super(message);
    this.message = message;
    this.name = "FontForgeError";
  }
}

class FontForgeCommandError extends Error {
  constructor(e: Error, cmd: string) {
    const msg =
      `FontForge command failed: ${e.toString()}\n` + `From command: ${cmd}`;
    super(msg);
    this.message = msg;
    this.name = "FontForgeError";
  }
}

function trim(s: string) {
  if (s.length === 0) return s;
  if (s[s.length - 1] === "\n") return s.slice(0, s.length - 1);
  return s;
}

export async function getName(src = "") {
  if (!src) throw new FontForgeError("fontforge.getName(): src not provided");
  const cmd = [
    "fontforge",
    "-lang=ff",
    "-c",
    "'Open($1); Print($fontname);'",
    `'${src}'`,
    "2>",
    "/dev/null",
  ].join(" ");
  let result = "";
  try {
    result = trim((await exec(cmd)).stdout);
  } catch (e) {
    throw new FontForgeCommandError(e as Error, cmd);
  }
  return result;
}

export async function addKerning(
  src: string,
  kerningPairs: {
    left: string;
    right: string;
    gap: number;
  }[]
) {
  if (!src)
    throw new FontForgeError("fontforge.addKerning(): src not provided");
  if (!kerningPairs)
    throw new FontForgeError(
      "fontforge.addKerning(): kerningPairs not provided"
    );

  // Add the kerning pairs
  const cmd = [
    `fontforge`,
    `-lang=ff`,
    `-c`,
    `'`,
    `Open($1);`,
    `AddLookup("kern","gpos_pair",0,[["kern",[["DFTL",["dflt"]]]]]);`,
    `AddLookupSubtable("kern","kern-1");`,
    kerningPairs
      .map((kerningPair) => {
        const { left, right, gap } = kerningPair;
        const leftCharUnicodeHex = `U+${left
          .charCodeAt(0)
          .toString(16)
          .padStart(4, "0")
          .toUpperCase()}`;
        const rightCharUnicodeHex = `U+${right
          .charCodeAt(0)
          .toString(16)
          .padStart(4, "0")
          .toUpperCase()}`;
        return [
          `Select("${rightCharUnicodeHex}");`,
          `second = GlyphInfo("Name");`,
          `Select("${leftCharUnicodeHex}");`,
          `AddPosSub("kern-1",second,0,0,${gap},0,0,0,0,0);`,
        ].join("");
      })
      .join(""),
    `Generate($1);`,
    `'`,
    `'${src}'`,
    `2>`,
    `/dev/null`,
  ].join(" ");

  let result = "";
  try {
    const out = (await exec(cmd)).stdout;
    result = trim(out);
  } catch (e) {
    throw new FontForgeCommandError(e as Error, cmd);
  }
  return result;
}

PascalPixel avatar Feb 06 '23 17:02 PascalPixel

Hello, Everyone! Commenting in hopes of bumping this back up - it would be great to get read/write support for kern data! @felipesanches can you comment as to the status of this?

mattlag avatar Jun 21 '23 18:06 mattlag

I believe that there was another PR on this that either got merged or was being looked at, but ill take a look and let you know. I've been busy with work.

ILOVEPIE avatar Jul 11 '23 03:07 ILOVEPIE

This is indeed a much needed feature! Do you think it will come with a future release?

quentin-f451 avatar Sep 30 '23 11:09 quentin-f451

Also writing a font from scratch and would love to be able to set simple kerning pairs 🙏🏼

Tenchi2xh avatar May 11 '24 16:05 Tenchi2xh