Skip to content

Instantly share code, notes, and snippets.

@rfde
Created May 17, 2020 14:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rfde/1037af52a7b4b5ecb8eff8ba5e0d9e42 to your computer and use it in GitHub Desktop.
Save rfde/1037af52a7b4b5ecb8eff8ba5e0d9e42 to your computer and use it in GitHub Desktop.
Anki addon to print all flashcards of the current deck in DIN A7 format, Anki 2.1 edition. (see https://gist.github.com/rfde/8aa6baac97e1ac8a9c8170c8c6d21ff8 for Anki < 2.1)
# -*- coding: utf-8 -*-
##############################################################################
#
# -Till Schlueter <mail@tschlueter.eu> 2020
# -GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
#
# -This add-on is a modified version of "Print as Flashcards" by Goal
# Oriented Academics LLC which is a modified version of the basic printing
# add-on by Damien Elmes.
#
# -With the desired deck selected, click "Make Flashcards (A7)" from the
# tools menu.
#
# -A file called flashcards.html will be created in your Anki profile folder.
# Your cards are arranged in a 4x2 grid, i.e. DIN A7 cards if printed on
# A4 paper borderless.
#
# -Print double-sided (flip on long edge). You may want to disable headers
# and footers in your browser's printing settings. I recommend using Google
# Chrome for printing (the generated HTML code needs improvement to work
# reliably with other browsers).
#
##############################################################################
import re, urllib
from aqt.qt import *
from anki.utils import isWin
from anki.hooks import runHook, addHook
from aqt.utils import openLink
from aqt import mw
from anki.utils import ids2str
# counter to enumerate cards
globalCardCounter = 1
def sortFieldOrderCids(did):
dids = [did]
for name, id in mw.col.decks.children(did):
dids.append(id)
return mw.col.db.list("""
select c.id from cards c, notes n where did in %s
and c.nid = n.id order by c.id""" % ids2str(dids))
def onPrint():
global globalCardCounter
# reset card counter
globalCardCounter = 1
# query card IDs
ids = sortFieldOrderCids(mw.col.decks.selected())
# query deck name
deckName = mw.col.decks.get(mw.col.decks.selected())['name']
def esc(s):
# Remove attributes from hr tag
s = re.sub("<hr.*?id=answer>", "<hr id=answer>", s)
# remove type answer
s = re.sub("\[\[type:[^]]+\]\]", "", s)
return s
def upath(path):
if isWin:
prefix = u"file:///"
else:
prefix = u"file://"
return prefix + urllib.parse.quote(path)
# array to collect the cards to be printed
cardsToPrint = []
mw.progress.start(immediate=True)
# for each card (ID)
for cid in ids:
# collect the following information about the card:
# question (front), answer (back), tags
# put them into a dictionary and append it to cardsToPrint.
currentCard = dict()
c = mw.col.getCard(cid)
cardContent = esc(c._getQA(True, False)['a']).split('<hr id=answer>')
currentCard["question"] = cardContent[0]
currentCard["answer"] = cardContent[1]
tags = c.note().stringTags().strip()
currentCard["tags"] = "-" if tags == "" else tags
cardsToPrint.append(currentCard)
# Pad last page with empty cards
if (len(cardsToPrint) % 8 != 0):
emptyCard = {"question": "", "answer": "", "tags": "-"}
for i in range(8-(len(cardsToPrint)%8)):
cardsToPrint.append(emptyCard)
# create html file
path = os.path.join(mw.pm.profileFolder(), "flashcards.html")
buf = open(path, "w")
# some general markup/html headers
buf.write("<!DOCTYPE html><html><head><title>flashcards</title>" + mw.baseHTML())
buf.write("""<meta charset=\'utf-8\' />
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3.0.1/es5/tex-mml-chtml.js"></script>
<style type="text/css">
body,html,tbody{height:100%}body,tbody,tr{width:100%}td,tr{height:25%}*{margin:0;padding:0}body{font-family:serif;font-size:12pt}td{width:50%;border:1px solid #ccc;box-sizing:border-box;-moz-box-sizing:border-box}div.flashcard,table{height:100%;width:100%}table{max-height:99.8%;border-collapse:collapse}div.tag{background-color:#888;position:absolute;padding-left:20px;padding-right:5px;font-size:10pt}div.flashcard{text-align:center;overflow:auto;display:flex;justify-content:center;align-items:center}div.flashcard-inner-front{padding-top:.9em}div.flashcard-inner-back{padding-top:.5em}img{max-width:100%;max-height:120px}img.latex{zoom:50%;margin-bottom:3px}
</style>
</head><body>""")
# for 8 cards per page...
for i in range(0, len(cardsToPrint), 8):
# generate a question page and the corresponding answer page
buf.write(makePage(cardsToPrint[i:i+8], False, deckName))
buf.write(makePage(cardsToPrint[i:i+8], True, deckName))
buf.write("</body></html>")
buf.close()
mw.progress.finish()
openLink(QUrl.fromLocalFile(path))
def makePage(cardsToPrint, isAnswerPage, deckName):
def makeFrontCard(card):
global globalCardCounter
res = '<div class="tag">'
res += deckName
res += " [%s, %s]" % (str(globalCardCounter), card["tags"])
globalCardCounter += 1
res += '</div><div class="flashcard"><div class="flashcard-inner-front">'
res += card["question"]
res += "</div></div>"
return res
def makeBackCard(card):
res = '<div class="flashcard"><div class="flashcard-inner-back">'
res += card["answer"]
res += "</div></div>"
return res
# We will create a table per page
pageContent = "<table><tbody>"
# Generate table content
# for each card…
for i in range(len(cardsToPrint)):
# left / right hand side?
if i % 2 == 0:
pageContent += "<tr><td>"
if isAnswerPage == False:
pageContent += makeFrontCard(cardsToPrint[i])
else:
# i+1 to swap answers horizontally
pageContent += makeBackCard(cardsToPrint[i+1])
pageContent += "</td>"
else:
pageContent += "<td>"
if isAnswerPage == False:
pageContent += makeFrontCard(cardsToPrint[i])
else:
# i-1 to swap answers horizontally
pageContent += makeBackCard(cardsToPrint[i-1])
pageContent += "</td></tr>"
# Close html tags and enforce page break
pageContent += '</tbody></table><div style="page-break-after:always"></div>'
return pageContent
q = QAction(mw)
q.setText("Make Flashcards (A7)")
mw.form.menuTools.addAction(q)
q.triggered.connect(onPrint)
{"name": "Print as Flashcards (A7)", "mod": 1589723493}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment