{"id":35153854,"url":"https://github.com/adobe-type-tools/mark-feature-helper-rf-ext","last_synced_at":"2026-05-19T08:32:06.406Z","repository":{"id":329015915,"uuid":"1117774305","full_name":"adobe-type-tools/mark-feature-helper-rf-ext","owner":"adobe-type-tools","description":"Guidance for creating OpenType mark features to use with the AFDKO + RoboFont extension","archived":false,"fork":false,"pushed_at":"2026-01-17T21:15:14.000Z","size":4178,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-15T22:57:06.655Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adobe-type-tools.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-16T19:47:04.000Z","updated_at":"2026-01-17T21:09:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/adobe-type-tools/mark-feature-helper-rf-ext","commit_stats":null,"previous_names":["adobe-type-tools/mark-feature-helper-rf-ext"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/adobe-type-tools/mark-feature-helper-rf-ext","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adobe-type-tools%2Fmark-feature-helper-rf-ext","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adobe-type-tools%2Fmark-feature-helper-rf-ext/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adobe-type-tools%2Fmark-feature-helper-rf-ext/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adobe-type-tools%2Fmark-feature-helper-rf-ext/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adobe-type-tools","download_url":"https://codeload.github.com/adobe-type-tools/mark-feature-helper-rf-ext/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adobe-type-tools%2Fmark-feature-helper-rf-ext/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33208150,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-19T07:54:09.561Z","status":"ssl_error","status_checked_at":"2026-05-19T07:54:08.508Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-28T16:11:31.330Z","updated_at":"2026-05-19T08:32:06.401Z","avatar_url":"https://github.com/adobe-type-tools.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mark Feature Helper / Mark Feature Example\n\nThis repository is an attempt at making the authoring of a mark feature a bit more accessible.\n\nIt consists of\n* a Mark Feature Helper Robofont Extension\n* an [example project](example_project/) implementing the mark feature\n\n---\n\nThe Mark Feature Helper looks like this:\n\n![Font window with Mark Feature Helper](img/font_window_with_helper.png)\n\nThe window basically is a little UI to the Adobe FDK’s [markFeatureWriter](https://github.com/adobe-type-tools/python-modules/tree/main?tab=readme-ov-file#markfeaturewriterpy) script.\n\n\nThere are two requirements for writing a mark feature:\n\n* base glyphs and marks with corresponding anchors\n* a group that helps the mark feature writer understand which glyphs are combining marks.\n\n## Functionality of the Mark Feature Helper\n\n![Mark Feature Helper window](img/helper_window.png)\n\n### Placing Anchors\n\n_This is not in the scope of this tool.\nThere have been many attempts at automating anchor placement, and most likely your UFO already has anchors. If it doesn’t, check Mechanic for the approach you like best._\n\nSome good extensions to help you here are:\n* [RoboFont Mark Positioning Tool by Bahman Eslami](https://gitlab.com/typoman/robofont-mark-tool)\n* [Anchor Dropper by Ryan Bugden](https://github.com/ryanbugden/Anchor-Dropper)\n* [Adjust Anchors by Adobe Type Tools](https://github.com/adobe-type-tools/adjust-anchors-rf-ext)\n\n\n### Building the `COMBINING_MARKS` group\n\nThe top of the helper has three buttons to help with defining a `COMBINING_MARKS` group in your UFO.\n\n* `[Show Existing Group]` – filter the font overview to see the contents of this group, if it already exists. In our test project there is no such group – therefore the button is greyed out.\n\n* `[Filter Eligible Glyphs]` – filter the font overview for glyphs that could make it into the `COMBINING_MARKS` group. Basically, this will be all zero-width glyphs with attaching anchors (`_top`, or `_above`, for example).  Be aware that this filter may find glyphs you really don’t want as combining marks.\n\n* `[Build Group From Selection]` – this button adds the `COMBINING_MARKS` group to your UFO, after you have carefully considered which glyphs to include.\n\n\n### Writing the `mark` feature file(s)\n\n* After our UFO has a `COMBINING_MARKS` group, the `[Write Feature File]` button will become active. When we click it, a `mark.fea` file will be written next to the UFO file.\n\n* Optionally, we can also write a `mkmk.fea` file – the contents of this file makes combinations like `x-acute-acute-acute-acute` possible.\n\n* Another option is to trim casing tags (`-t` option in `markFeatureWriter`) – this is a very Adobe-ish approach and has to do with Latin being a bicameral script. More in the “ins and outs” section below.\n\n\n#### NB\nBoth `mark.fea` and `mkmk.fea` are written in the “meat without the sandwich” style typical for the FDK. To test/compile the features, they need to be wrapped in feature “fences”:\n\n```fea\nfeature mark {\n    include(mark.fea);\n} mark;\n\nfeature mkmk {\n    include(mkmk.fea);\n} mkmk;\n\n```\n\nThe reason for this is to allow re-writing the features without having to edit a big feature tree.\n\n\n## Ins and Outs of Mark Feature Writing\n\nWriting a mark feature is a joyful activity, however there a number of caveats to consider.  \nWith a simple `mark.fea`, our example font will look like this:\n\n![mixed marks](img/marks_lc_only.png)\n\nWhat happened? \n* We entered the glyphs `A` `U+0301` `B` `U+0301` `C` `U+0301` `D` `U+0301` `a` `U+0301` `b` `U+0301` `x` `U+0301`\n* The mark feature is working as expected (in some cases), attaching the combining acute (U+0301) to the anchor on the base glyph\n* The layout engine is smart and thinks – “Ah! A combination of A and Acute! I know this and will therefore present the glyph for Aacute (U+00C1)” (I think that’s _Unicode normalization_)\n\nLooking at the mark feature, we understand why:\n\n```fea\nmarkClass acutecmb \u003canchor 0 472\u003e @MC_aboveLC;\nmarkClass acutecmb.cap \u003canchor 0 690\u003e @MC_aboveUC;\n\nlookup MARK_BASE_aboveLC {\n    pos base a \u003canchor 219 472\u003e mark @MC_aboveLC;\n    pos base b \u003canchor 147 744\u003e mark @MC_aboveLC;\n    pos base c \u003canchor 246 472\u003e mark @MC_aboveLC;\n} MARK_BASE_aboveLC;\n\nlookup MARK_BASE_aboveUC {\n    pos base A \u003canchor 304 690\u003e mark @MC_aboveUC;\n    pos base B \u003canchor 270 690\u003e mark @MC_aboveUC;\n    pos base C \u003canchor 345 690\u003e mark @MC_aboveUC;\n} MARK_BASE_aboveUC;\n```\n\nWe have two mark `lookup`s, and two mark classes, with the lowercase marks attaching to the lowercase base glyphs, and the uppercase marks attaching to the uppercase base glyphs. Since the uppercase marks don’t have Unicode code points, there is just no way of typing them.\n\n\nHow to solve this problem?\n* This is where the “trim casing tags” flag comes in. In many Adobe projects, anchors are assigned as `aboveUC` and `aboveLC`. This has the benefit that they can be used to combine accented glyphs within RoboFont. (`aboveUC` accents go on top of the caps, and `aboveLC` accents go on top of the lowercase). \n* Obviously, trimming casing tags is not necessary if you only use a single `top`/`_top` anchor pair for both upper- and lowercase.\n* When trimming casing tags from `aboveUC`/`aboveLC` while writing the mark feature file, we end up with a simplified feature like this:\n* When using the `markFeatureWriter` on the command line, casing tags can be trimmed with option `-t`.\n\n```fea\nmarkClass acutecmb \u003canchor 0 472\u003e @MC_above;\nmarkClass acutecmb.cap \u003canchor 0 690\u003e @MC_above;\n\nlookup MARK_BASE_above {\n    pos base A \u003canchor 304 690\u003e mark @MC_above;\n    pos base B \u003canchor 270 690\u003e mark @MC_above;\n    pos base C \u003canchor 345 690\u003e mark @MC_above;\n    pos base a \u003canchor 219 472\u003e mark @MC_above;\n    pos base b \u003canchor 147 744\u003e mark @MC_above;\n    pos base c \u003canchor 246 472\u003e mark @MC_above;\n} MARK_BASE_above;\n\n```\n\nOnly one mark `lookup`, and a single mark class with both lowercase and uppercase marks attaching to the lowercase and uppercase base glyphs. The result:\n\n![mixed marks](img/mix_of_marks.png)\n\nWhat happened? \n* We entered the glyphs `A` `U+0301` `B` `U+0301` `C` `U+0301` `D` `U+0301`\n* The mark feature is working as expected, attaching the combining acute (U+0301) to the anchor on the base glyph\n* Pre-composed accented are normalized, while novel combinations just attach the literal combining mark. Since the normalized accented capitals are most likely composed with alternate accents (shallower angle, slightly smaller), this results in a mixed appearance.\n\n\nHow to solve this problem?\n* We need to substitute marks on the fly – so the correct accent is attached to the respective base glyph. This can be done in the `ccmp` feature:\n\n```fea\n# very simplified, extended version in attached example project\nfeature ccmp {\n    lookup CAP_ACCENT_SWAP {\n        sub [ A B C D ] acutecmb' by acutecmb.cap;\n    } CAP_ACCENT_SWAP;\n\n} ccmp;\n```\n\nThe result:\n![marks influenced by the ccmp feature](img/marks_with_ccmp.png)\n\nAmazing – the caps now all have uniform acutes. Check the b – probably you’d want a flatter acute there as well, which will make the `ccmp` a bit more complicated.\n\n---\n\nIs that all?\nNot quite, there’s another caveat:\n\n![kerning interrupted by marks](img/kern_without_lookupflag.png)\n\nAs we can see, the kerning between T and o is interrupted by the insertion of a combining mark. Fortunately, all we have to do is insert a `lookupflag` into the kerning like this:\n\n```fea\nfeature kern {\n    lookup KRN {\n        lookupflag IgnoreMarks;\n        include(kern.fea);\n    } KRN;\n} kern;\n```\n\n![kerning with lookupflag](img/kern_with_lookupflag.png)\n\n---\n\nIf you’ve read this far, you are truly re`mark`able … 🫣\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadobe-type-tools%2Fmark-feature-helper-rf-ext","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadobe-type-tools%2Fmark-feature-helper-rf-ext","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadobe-type-tools%2Fmark-feature-helper-rf-ext/lists"}