Created
October 19, 2018 12:12
-
-
Save rfde/8aa6baac97e1ac8a9c8170c8c6d21ff8 to your computer and use it in GitHub Desktop.
Anki addon to print all flashcards of the current deck in DIN A7 format.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
############################################################################## | |
# | |
# -Till Schlueter <mail@tschlueter.eu> 2018 | |
# -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 getBase, openLink, tooltip | |
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 + unicode( | |
urllib.quote(path.encode("utf-8")), "utf-8") | |
# 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>" + getBase(mw.col).encode("utf8")) | |
buf.write("""<meta charset=\'utf-8\' /> | |
<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).encode("utf-8")) | |
buf.write(makePage(cardsToPrint[i:i+8], True, deckName).encode("utf-8")) | |
buf.write("</body></html>") | |
buf.close() | |
mw.progress.finish() | |
tooltip(_("Loading..."), period=1000) | |
QDesktopServices.openUrl(QUrl.fromEncoded(upath(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) | |
mw.connect(q, SIGNAL("triggered()"), onPrint) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment