{
  "id": 15318139,
  "title": "Why your generated Anki image occlusion cards import as plain cloze (and the one-line fix)",
  "url": "https://urgent.news/2026/10/18/why-your-generated-anki-image-occlusion-cards-import-as-plain-cloze",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-10-18T00:52:48.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/lxhsoluto-star/why-your-generated-anki-image-occlusion-cards-import-as-plain-cloze-and-the-one-line-fix-3hj3"
  },
  "original_language": "en",
  "account": "AnkiForge is a web application that transforms lecture PDFs and labeled diagrams into Anki cards. The process involves creating image occlusion cards, with each label on a diagram hidden, resulting in a card for each label. The primary challenge lies in positioning the masks correctly. Achieving Anki to recognize these masks as genuine image occlusion required a single line of configuration, though the implications of that line demanded further investigation - which involved delving into Anki's source code twice.\n\nAnki has incorporated image occlusion since version 23.10, although initially, it was available as an add-on called Image Occlusion Enhanced. This add-on came with its own note type and numerous shared decks still utilize this older format. The Anki user forums frequently receive inquiries such as \"Anki deck with image occlusion shows no images\" from individuals who have imported such decks. When generating these decks programmatically, the simplest method is to replicate the templates and field names of the built-in Image Occlusion note type. Consequently, a cloze note type is generated, albeit with the misleading name \"Image Occlusion.\" Remarkably, the cards function as intended, reviewing well because the template draws the masks itself.\n\nHowever, the issue surfaces when a person opens a card to modify a mask. In a desktop view (specifically, aqt/editor_legacy.py from the 26.9 package), Anki determines if it should display the mask editor through the following function:\n\ndef current_notetype_is_image_occlusion(self):\nbool:\nif not self . note:\nreturn False\nreturn ( self . note_type (). get ( originalStockKind , None ) == StockNotetype . OriginalStockKind . ORIGINAL_STOCK_KIND_IMAGE_OCCLUSION )\n\nThe critical value determining this is ORIGINAL_STOCK_KIND_IMAGE_OCCLUSION, which equals 6 in a schema 11 collection. When this value is absent, the image occlusion button remains hidden, and the editor displays plain text fields, revealing the raw {{c1::image-occlusion:rect:...}} string where masks should be.\n\nWithin a schema 11 collection, note types are stored as JSON in the col table, manifesting as follows in my case:\n\n{\nname : \"Image Occlusion (AnkiForge)\",\ntype : 1,\n// the crucial line\noriginalStockKind : 6,\nflds : [\nfield(\"Occlusion\", 0,\n{ tag : 0, preventDeletion : true }),\nfield(\"Image\", 1,\n{ tag : 1, preventDeletion : true }),\nfield(\"Header\", 2,\n{ tag : 2, preventDeletion : true }),\nfield(\"Back Extra\", 3,\n{ tag : 3, preventDeletion : true }),\nfield(\"Comments\", 4,\n{ tag : 4, preventDeletion : false }),\n],\n// templates copied from Anki's stock IO note type\n}\n\nThe field tags and preventDeletion flags are copied from the stock note type, ensuring the generated note matches the built-in one field by field. The occlusion field is a string, precisely copied from each mask. Each mask is represented as a cloze deletion with a shape inside it, where coordinates are fractions of the image size (e.g., {{c1::image-occlusion:rect:left=.2325:top=.1104:width=.1371:height=.0412}}). The shape coordinates are formatted to four decimal places and then trimmed of leading and trailing zeros. For \"Hide all, guess one\" mode, each shape is appended with :oi=1. I meticulously matched this output character for character when copying the note into my export, ensuring that a note from my export and the identical note after passing through Anki's editor are indistinguishable. However, I refrained from testing Anki's parser's tolerance for alternative formats, preferring not to encounter potential issues from users. To validate my approach, I tested the import process using Anki itself, importing the generated .apkg into a fresh collection and subsequently extracting the occlusions via get_image_occlusion_note, Anki's call to load a note's masks. Following the edit to remove the flag from a test deck, the test still succeeded. This backend parsing confirmed that the absence of the flag does not prevent the parsing of occlusions. However, only the desktop UI reads this flag, so a backend-only test cannot detect its absence. The test has been updated to perform this check directly: the collection is instantiated, the .apkg is imported, and for each note type, the function checks if the note contains the \"Occlusion\" field. If successful, it asserts that the note's originalStockKind is 6, signaling that Anki's editor will recognize it as an image occlusion card.",
  "summary": "I've been building AnkiForge , a web app that turns lecture PDFs and labelled diagrams into Anki cards. For diagrams it makes image occlusion cards: every label on, say, a heart diagram gets hidden, one card per label. Getting the masks in the right place took most of the work. Getting Anki to treat them as real image occlusion took one line of config, and finding out what that line actually does…",
  "key_points": [
    "Anki generates image occlusion cards from lecture PDFs and diagrams",
    "Missing ORIGINALSTOCKKINDIMAGEOCCLUSION flag hides image occlusion button",
    "Removing flag allows desktop UI to recognize cards as image occlusion"
  ],
  "editors_take": "Programmatic card generation requires specific metadata flags to ensure the Anki desktop editor recognizes masks as interactive elements rather than displaying them as uneditable plain text strings.",
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}