diff options
author | Karl Berry <karl@freefriends.org> | 2021-01-05 22:05:39 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-01-05 22:05:39 +0000 |
commit | 17b165bb23c1130b25b0d82e6eb22e9323e2a5a4 (patch) | |
tree | 043e9a925408b6b8c9efb46366957a57b3a17941 /Master/texmf-dist/source/latex/moodle | |
parent | c835c59abef4c26ab3c03d6e9a0bc1a0f4d1c202 (diff) |
moodle (5jan21)
git-svn-id: svn://tug.org/texlive/trunk@57334 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/moodle')
-rw-r--r-- | Master/texmf-dist/source/latex/moodle/makefile | 90 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/moodle/moodle.dtx | 3975 | ||||
-rw-r--r-- | Master/texmf-dist/source/latex/moodle/moodle.ins | 8 |
3 files changed, 3522 insertions, 551 deletions
diff --git a/Master/texmf-dist/source/latex/moodle/makefile b/Master/texmf-dist/source/latex/moodle/makefile new file mode 100644 index 00000000000..d3bf3576f7b --- /dev/null +++ b/Master/texmf-dist/source/latex/moodle/makefile @@ -0,0 +1,90 @@ +PROJECT_NAME=moodle +FILE_CLEAN=*.sty *.log *.aux *.auxlock *.out *.blg *.bbl *.toc *.xml *.bcf *.synctex.gz *~ *.nav *.snm *.idx *.ilg *.ind _minted-* *.glo *.gls *.dpth +LATEX=latex +PDFLATEX=lualatex +LATEXFLAGS=-interaction=nonstopmode -shell-escape +DIFFTOOL=meld +MAKEINDEX=makeindex +TESTDIR=test +RECURSIVE_TARGETS= all clean distclean test +RERUNLATEX= '(There were undefined references|Rerun to get (cross-references|the bars) right)' + +ifndef DEBUG + DEBUG=> /dev/null +endif + +.DEFAULT_GOAL := all + +.PHONY: all clean distclean test dist install + +all: $(PROJECT_NAME).pdf $(PROJECT_NAME).sty + +clean: $(TESTDIR)/clean + rm -rf $(FILE_CLEAN) + +distclean: clean + +distclean: $(TESTDIR)/distclean + rm -rf *.pdf *.xml *.zip + rm -rf texmf/ $(@mkdir -p ./$(PROJECT_NAME)/ + +test: $(TESTDIR)/test + +dist: test $(PROJECT_NAME).zip + @mv $(PROJECT_NAME).zip $(PROJECT_NAME)_$(shell date +"%Y-%m-%d").zip + @mv $(PROJECT_NAME).tds.zip $(PROJECT_NAME)_$(shell date +"%Y-%m-%d").tds.zip + @cp $(PROJECT_NAME).pdf $(PROJECT_NAME)_$(shell date +"%Y-%m-%d").pdf + @$(MAKE) clean -C . + +$(PROJECT_NAME).zip: $(PROJECT_NAME).tds.zip $(PROJECT_NAME).pdf + @mkdir -p ./$(PROJECT_NAME)/ + @cp $(PROJECT_NAME).dtx ./$(PROJECT_NAME)/ + @cp $(PROJECT_NAME).ins ./$(PROJECT_NAME)/ + @cp $(PROJECT_NAME).pdf ./$(PROJECT_NAME)/ + @cp README.ctan ./$(PROJECT_NAME)/README.md + zip -r9 $(PROJECT_NAME).zip $(PROJECT_NAME).tds.zip $(PROJECT_NAME) + +$(PROJECT_NAME).tds.zip : $(PROJECT_NAME).pdf $(PROJECT_NAME).sty + @mkdir -p ./texmf/tex/latex/$(PROJECT_NAME)/ + @mkdir -p ./texmf/source/latex/$(PROJECT_NAME)/ + @mkdir -p ./texmf/doc/latex/$(PROJECT_NAME)/ + @cp $(PROJECT_NAME).sty ./texmf/tex/latex/$(PROJECT_NAME)/ + @cp $(PROJECT_NAME).dtx ./texmf/source/latex/$(PROJECT_NAME)/ + @cp $(PROJECT_NAME).ins ./texmf/source/latex/$(PROJECT_NAME)/ + @cp makefile ./texmf/source/latex/$(PROJECT_NAME)/ + @cp $(PROJECT_NAME).pdf ./texmf/doc/latex/$(PROJECT_NAME)/ + @cp README.ctan ./texmf/doc/latex/$(PROJECT_NAME)/README.md + @cp LICENSE ./texmf/doc/latex/$(PROJECT_NAME)/ + @$(MAKE) distclean -C $(TESTDIR) + @rsync -avq --exclude='$(TESTDIR)/extra' --exclude='$(TESTDIR)/media' --exclude='$(TESTDIR)/dev_*.tex' $(TESTDIR) ./texmf/doc/latex/$(PROJECT_NAME) + @cd texmf/ ; zip -r9 ../$(PROJECT_NAME).tds.zip tex source doc + +install: $(PROJECT_NAME).tds.zip + @unzip -d ~/texmf ./$(PROJECT_NAME).tds.zip + +$(PROJECT_NAME).sty: $(PROJECT_NAME).dtx + +$(PROJECT_NAME).pdf: $(PROJECT_NAME).dtx $(PROJECT_NAME).gls + +%.glo: %.dtx + $(PDFLATEX) $(LATEXFLAGS) $< $(DEBUG) + +%.gls: %.glo + $(MAKEINDEX) -s gglo.ist -o $@ $< $(DEBUG) + +%.pdf: %.tex + $(PDFLATEX) $(LATEXFLAGS) $< $(DEBUG) + +%.pdf: %.dtx + $(PDFLATEX) $(LATEXFLAGS) $< $(DEBUG) + @if egrep -q $(RERUNLATEX) $(basename $@).log ; then \ + echo "\trequires a new $(PDFLATEX) pass..."; \ + $(PDFLATEX) $(LATEXFLAGS) $< $(DEBUG); \ + fi + +%.sty: %.ins + rm -rf $@ + $(LATEX) $(LATEXFLAGS) $< $(DEBUG) + +$(RECURSIVE_TARGETS:%=$(TESTDIR)/%) : + @$(MAKE) $(notdir $@) -C $(dir $@) diff --git a/Master/texmf-dist/source/latex/moodle/moodle.dtx b/Master/texmf-dist/source/latex/moodle/moodle.dtx index 02b43f79238..17a7b125b41 100644 --- a/Master/texmf-dist/source/latex/moodle/moodle.dtx +++ b/Master/texmf-dist/source/latex/moodle/moodle.dtx @@ -1,7 +1,8 @@ % \iffalse meta-comment % % moodle.dtx -% Copyright 2016 by Anders O.F. Hendrickson (anders.hendrickson@snc.edu) +% Copyright 2016 by Anders O.F. Hendrickson (anders.o.f.hendrickson AT gmail.com) +% and 2019-2021 by Matthieu Guerquin-Kern (guerquin-kern AT crans.org). % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 @@ -13,7 +14,7 @@ % % This work has the LPPL maintenance status `maintained'. % -% The Current Maintainer of this work is Anders O.F. Hendrickson. +% The Current Maintainer of this work is Matthieu Guerquin-Kern. % % This work consists of the files moodle.dtx and moodle.ins % and the derived files moodle.sty and getitems.sty. @@ -27,27 +28,37 @@ %<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01] %<package>\ProvidesPackage{moodle} %<*package> - [2016/01/11 v0.5 Moodle quiz XML generation] + [2021/01/04 v0.8 Moodle quiz XML generation] %</package> % %<*driver> -\documentclass{ltxdoc} -\usepackage{moodle} -\usepackage{amssymb} -\usepackage{fancyvrb} +\documentclass[a4paper]{ltxdoc} +\usepackage{iftex} +\ifPDFTeX + \usepackage[utf8]{inputenc} % necessary + \usepackage[T1]{fontenc} % necessary + \usepackage[protrusion=true,expansion]{microtype} + \usepackage[ttscale=.875]{libertine} +\else % assuming LuaLaTeX or XeLaTeX + \usepackage{fontspec} + \setmainfont[Mapping=tex-text]{Linux Libertine O} +\fi +%\usepackage{moodle}[2020/07/09] +\usepackage{amssymb,metalogo,multirow,threeparttable,booktabs,hyperref,tikz,minted,changelog} +\usetikzlibrary{arrows,positioning,decorations.text} +%\usemintedstyle{Wombat} \EnableCrossrefs \CodelineIndex \OnlyDescription \RecordChanges \begin{document} \DocInput{moodle.dtx} - \PrintChanges - %\PrintIndex + %\PrintChanges \end{document} %</driver> % \fi % -% \CheckSum{3827} +% \CheckSum{7442} ^^A Comment \OnlyDescription above to adjust % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z @@ -66,9 +77,7 @@ % Right brace \} Tilde \~} % % -% \changes{v0.5}{2016/01/05}{Initial version} -% -% \GetFileInfo{moodle.sty} +% \GetFileInfo{\jobname.dtx} % % \DoNotIndex{\newcommand,\newenvironment,\def} % @@ -77,8 +86,12 @@ % generating Moodle quizzes via \LaTeX% % \thanks{This document % corresponds to \textsf{moodle.sty}~\fileversion, dated \filedate.}} -% \author{Anders Hendrickson\\ St.~Norbert College, De~Pere, WI, USA \\ \url{anders.hendrickson@snc.edu}} -% \date{January 11, 2016} +% \author{Anders Hendrickson\footnote{original author of the package (\texttt{v0.5}), inactive.}\\ +% \texttt{anders.o.f.hendrickson AT gmail.com} \and +% Matthieu Guerquin-Kern\footnote{current maintainer, author of the updates (\texttt{v0.6} +% to \texttt{v0.8}), partially funded by \href{https://www.ensea.fr/en/}{ENSEA Graduate School}, France.}\\ +% \href{mailto:guerquin-kern AT crans.org}{guerquin-kern AT crans.org}} +% \date{January 4, 2021} % % \maketitle % @@ -87,8 +100,8 @@ % \section{Motivation} % % The acronym Moodle stands for ``Modular Object-Oriented Dynamic Learning Environment.'' -% It is an open source learning management system employed by many universities, colleges, -% and high schools to provide digital access to course materials, such as +% It is an open source learning management system (LMS) employed by many universities, +% colleges, and high schools to provide digital access to course materials, such as % notes, video lectures, forums, and the like; see % \url{https://moodle.com/moodle-lms/} for more information. % One of the many useful @@ -148,28 +161,82 @@ % and the PDF may be previewed onscreen or printed out for ease of proofreading. % Mathematical graphics can be integrated within the main file through \TikZ, % and of course \LaTeX\ macros can be customized. -% Using the present \textsf{moodle.sty} package, +% Using the present \textsf{moodle} package, % a quiz author can type a quiz using familiar \LaTeX\ syntax and document % structure. Upon compilation, \LaTeX\ will generate both a well-organized % PDF that is easy to proofread and an XML file that can be uploaded directly % to Moodle. The entire process is far faster than using Moodle's own % web-based editor, makes it easier to catch one's mistakes, % and the ultimate source code of one's work is a human-readable |.tex| file -% that can be archived, browsed, and edited offline. +% that can be archived, versioned, browsed, and edited offline. +% +% Strictly speaking, the \textsf{moodle} package does not generate quizzes: +% it generates question banks that can be imported in the LMS. The teacher +% still needs to compose manually a quiz from the question banks. Hopefully, +% two Moodle features supported by the package make this task easier: +% categories and tags. +% +% In this documentation the LMS is referred to as Moodle (uppercase M and +% roman font) while the \LaTeX\ package that is documented here is referred +% to as \textsf{moodle} (all lower case and sans serif font). % % \section{Workflow} -% The process of creating a quiz in Moodle using this package is as follows: +% The process of creating a quiz in Moodle using this package is depicted in +% Figure~\ref{fig:workflow}. It follows a few steps: % \begin{enumerate} -% \item Write a \LaTeX\ document using |\usepackage{moodle}| as described below. -% \item Compile the document to PDF using pdf\LaTeX. This will also produce the file -% \meta{jobname}|-moodle.xml|. +% \item Write a \LaTeX\ document using |\usepackage{moodle}| as described +% below. +% \item Compile the document to PDF using pdf\LaTeX\, \XeLaTeX\ or \LuaLaTeX. +% This will also produce the file \meta{jobname}|-moodle.xml|. % \item Open Moodle, navigate to the desired course, % and under ``Question bank'' select ``Import.'' % \item Select ``Moodle XML format,'' choose the XML file to upload, and press ``Import.'' % \item After Moodle verifies that the questions have been imported correctly, % you may add them to your quizzes. % \end{enumerate} -% +% \begin{figure}[tbp] +% \centering +% \begin{tikzpicture}[node distance=1,auto,bend +% angle=45,box/.style={rectangle,draw=blue!50,rounded corners=3,top +% color=white,bottom color=black!20,thick,align=center,text +% width=2.5cm},elmt/.style={font=\itshape,align=left}, +% cmnt/.style={font=\footnotesize,align=center}, +% bigcmnt/.style={font=\normalsize,align=center},pre/.style={<-,>=stealth',thick}, +% post/.style={->,>=stealth',thick}, prepost/.style={<->,>=stealth',thick}] +% \draw[white,fill=orange!20,rounded corners=10] +% (-1.5,-.7)--++(0,1.4)--++(10.3,0)--++(0,-3.3)--++(-6.7,0)--++(-.25,1.725)--cycle; +% \node[orange!80!black,above] (dev) at (7.5,-2.6) {\textit{Developpement}}; +% \fill[green,fill opacity=.2,rounded corners=10] +% (-1.5,.7)--++(3,0)--++(0.5,-3.4)--++(6.8,0)--++(0,-2)--++(-10.3,0)--cycle; +% \node[green!80!black,below] (dev) at (7.5,-2.7) {\textit{Publishing}}; +% \node[box,text width=2cm] (tex) {\texttt{.tex} source file}; +% \node[rectangle,fill=white,draw,align=center,text +% width=1.5cm,below=of tex] (compfinal) {\LaTeX{}\\ engine}; +% \node[rectangle,fill=white,draw,align=center,text width=1.5cm,right=of tex,xshift=4.5cm] +% (compdraft) {\LaTeX{}\\ engine}; +% \node[box,right=of compfinal,xshift=.5cm] (pdf) {\texttt{.pdf} file for proofreading}; +% \node[rectangle,fill=white,draw,align=center,text width=2cm,below=of compfinal] (extern) +% {Picture\\processing}; +% \draw (pdf|-extern) node[box,anchor=center] (xml) {\texttt{.xml} file\\\footnotesize(pictures embedded)}; +% \draw (compdraft|-xml) +% node[rectangle,fill=white,draw,align=center,text width=1.5cm,anchor=center] (moodle) +% {Moodle\\Server}; +% \draw (tex) edge [post,bend right=10] node[cmnt,pos=.5,left] {\texttt{final}} (compfinal); +% \draw (tex) edge [post,bend left=10] node[cmnt,pos=.5,below] {\texttt{draft}} (compdraft); +% \draw (compdraft) edge [post,bend left=10] (pdf); +% \draw (compfinal) edge [post] (pdf); +% \draw (compfinal) edge [post,bend left=10] (xml); +% \draw (compfinal) edge [post,bend right=30] node[cmnt,black!40,pos=.5,text width=1cm,left] +% {\texttt{tikz}, \texttt{.png}, \texttt{.jpg}} (extern); +% \draw (extern) edge [post,green,bend right=30] node[cmnt,black!40,pos=.5,below,right] +% {\texttt{base64}} (compfinal); +% \draw (pdf) edge [post,red,bend right=15] node[sloped,cmnt,pos=.5,above] {improve} (tex); +% \draw (xml) edge [post,red] node[cmnt,pos=.5,below] {import} (moodle); +% \end{tikzpicture} +% \caption{Block diagram describing a typical workflow using the \textsf{moodle} package.} +% \label{fig:workflow} +% \end{figure} +% % \section{Usage}\label{sect:usage} % \subsection{Example Document} % @@ -177,35 +244,34 @@ % and editing Moodle quizzes through the web interface. % The |xkeyval| package is used to provide a key-value interface. % Here is a simple example document: -% \begin{verbatim} +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} % \documentclass[12pt]{article} -% \usepackage{moodle} -% +% \usepackage[section]{moodle} +% \moodleregisternewcommands +% \newcommand\monomial[1]{x^{#1}} +% \newcommand\sillyanswer{What!?} % \begin{document} -% % \begin{quiz}{My first quiz} % \begin{numerical}[points=2]{Basic addition} % What is $8+3$? % \item 11 % \end{numerical} -% -% \begin{shortanswer}[case sensitive=true]{Newton's name} +% \begin{shortanswer}[usecase]{Newton's name} % What was Newton's first name? % \item Isaac -% \item[fraction=0, feedback={No, silly!}] Fig -% \item{fraction=0} Sir +% \item[fraction=0, feedback={\sillyanswer}] Fig +% \item[fraction=0] Sir % \end{shortanswer} -% % \begin{multi}[points=3]{A first derivative} -% What is the first derivative of $x^3$? -% \item $\frac{1}{4} x^4+C$ -% \item* $3x^2$ -% \item $51$ +% What is the first derivative of $\monomial{3}$? +% \item $\frac{1}{4}\monomial{4}+C$ +% \item[feedback={yes!}]* $3\monomial{2}$ +% \item[feedback={\sillyanswer}] $51$ % \end{multi} % \end{quiz} -% % \end{document} -% \end{verbatim} +% \end{VerbatimOut} +% \inputminted[gobble=2,frame=lines]{latex}{minted.doc.out} % Key features to note in this first example are that a |quiz| environment % contains several question environments. % Each question takes a name as a mandatory argument, @@ -214,39 +280,102 @@ % such as |itemize| or |enumerate|, in that answers are set off by % |\item|'s, but the question itself is the text that occurs before % the first |\item|. +% +%^^A \DescribeMacro{\moodleregisternewcommands}\DescribeMacro{\htmlregister} +%^^A Calling |\moodleregisternewcommands| tells the package to treat +%^^A specifically the macros defined subsequently. +%^^A This way, the macros that Moodle's \LaTeX\ renderer does not know +%^^A about can be properly expanded in the XML file. +%^^A This mechanism applies only to the macros defined using |\newcommand| +%^^A and \emph{without} optional argument. +%^^A Using |\htmlregister|\marg{macroname}, lets you declare a specific +%^^A macro for expansion. This mechanism, instead, also applies to the +%^^A macros defined using \TeX's primitive |\def|. % +% \subsection{Package Options} % \DescribeMacro{draft} -% If the package option |draft| is invoked, by calling |\usepackage[draft]{moodle}|, -% then no XML file will be generated. This is especially useful while editing a quiz -% containing graphics, so as to avoid the time spent converting image files. +% \DescribeMacro{final} +% If the package option |draft| is invoked, by calling +% |\usepackage[draft]{moodle}| or |\documentclass[draft]{...}|, then no +% XML file will be generated. This is especially useful while editing a +% quiz containing graphics, so as to avoid the time spent converting +% image files. The package option |final| might be useful if one wants +% to avoid the option |draft| to be inherited from the |documentclass|. +% +% \DescribeMacro{handout} +% If the package option |handout| is invoked (|\usepackage[handout]{moodle}|), +% the PDF file is generated clean from teacher-only information (answers, +% points, penalty, feedback, tags) and, hence, can be given to students for +% classroom work. In particular, as would Moodle do, answers in +% \texttt{matching} questions are shuffled and the option \texttt{shuffle} +% triggers the shuffling of choices offered (\texttt{multi} and +% \texttt{matching}). This is achieved thanks to the package +% \texttt{randomlist}, loaded if the option is invoked. +% The XML file is generated as usual. +% +% \DescribeMacro{nostamp} +% By default, the package will output a stamp as a comment in the XML +% file. This stamp contains information gathered about the TeX engine, +% the operating system used and the package version. For instance: +% \begin{quote}\small +% |<!-- This file was generated on 2020-11-30 by LuaLaTeX -->|\\ +% |<!-- running on Linux with the package moodle |\texttt{\fileversion}| -->| +% \end{quote} +% The package option +% |nostamp| prevents this stamp to be written in the XML file. +% +% \DescribeMacro{section} +% \DescribeMacro{section*} +% \DescribeMacro{subsection} +% \DescribeMacro{subsection*} +% If the package option |section| is invoked (|\usepackage[section]{moodle}|), +% then each quiz is represented by a different \LaTeX\ section. Starred +% variants correspond to unnumbered sections or subsections. To preserve +% compatibility with Version 0.5 of this package, the default is |subsection*|. +% Consequently, |\usepackage{moodle}| is equivalent to +% |\usepackage[subsection*]{moodle}|. +% +% \DescribeMacro{tikz} The package option \texttt{tikz} is described in section +% \ref{subsec:tikz}. +% +% \DescribeMacro{svg} The package option \texttt{svg} is described in section +% \ref{subsec:svg}. % -% \newpage % \subsection{Quiz and Question Environments} % % \DescribeMacro{quiz} % A |.tex| document to generate Moodle quizzes contains one or more % |quiz| environments, within which various question environments are nested. -% The required argument to the |quiz| environment names the ``question bank'' -% to which the questions inside will belong after being uploaded to moodle. +% The required argument to the |quiz| environment names a category for Moodles +% ``question bank'': after import, the questions defined in this environment +% will be gathered in this category. % \begin{center} -% |\begin{quiz}|\oarg{options}\marg{question bank name}% +% |\begin{quiz}|\oarg{options}\marg{category name}% % \end{center} % There are no |quiz|-specific options, % but any \meta{options} set with |\begin{quiz}| % will be inherited by all questions contained within that |quiz| environment. % % \DescribeMacro{\moodleset} -% Options may also be set at any time with |\moodleset|\marg{options}; -% these changes are local to \TeX-groups. -% \bigskip +% Options may also be set outside question environments with +% |\moodleset|\marg{options}; these changes are local to \TeX-groups. +% +% \DescribeMacro{\setcategory}\DescribeMacro{\setsubcategory} +% Although the |quiz| environment defines a category by its own, one can change +% the current category inside the quiz environment and in between questions, +% using the macro |\setcategory|\marg{category name}. A subcategory can also be +% defined with |\setsubcategory|\marg{subcategory name}. The categories and +% subcategories are reflected in the PDF file as sections, subsections, or +% subsubsections, in accordance to the package setting |section|, |section*|, +% |subsection|, or |subsection*|. % % The syntax for each question environment is % \begin{quote} % |\begin|\marg{question type}\oarg{question options}\marg{question name} \\ % \rule{2em}{0pt}\meta{question text} \\ -% \rule{2em}{0pt}|\item| \meta{item} \\ +% \rule{2em}{0pt}|\item|\oarg{item options} \meta{item} \\ % \rule{2em}{0pt}\quad$\vdots$ \\ -% \rule{2em}{0pt}|\item| \meta{item} \\ +% \rule{2em}{0pt}|\item|\oarg{item options} \meta{item} \\ % |\end|\marg{question type} % \end{quote} % The meaning of the \meta{item}s varies depending on the question type, @@ -268,13 +397,54 @@ % \DescribeMacro{fraction} % In most question types, it is possible to designate some answers as being % worth partial credit---that is, some fraction of a completely correct answer. -% The |fraction| key may be set to any integer from |0| (entirely wrong) to |100| (entirely correct). +% The |fraction| key may be set to any of the values given in Table~\ref{tab:fraction}, +% from |0| (entirely wrong) to |100| (entirely correct). +% +% \begin{table}[tbp] +% \centering +% \caption{\href{https://github.com/moodle/moodle/blob/MOODLE\_310\_STABLE/question/engine/bank.php\#L339}{Valid positive options} for the \texttt{fraction} key: $100\cdot(p/q)$.} +% \label{tab:fraction} +% \footnotesize +% \begin{tabular}{l*{10}{l}} +% \toprule +% Denominator $q$ & \multicolumn{10}{c}{Numerator $p$}\\ +% \cmidrule{2-11} +% & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9\\ +% \cmidrule(lr){1-1}\cmidrule(lr){2-2}\cmidrule(lr){3-3}\cmidrule(lr){4-4} +% \cmidrule(lr){5-5}\cmidrule(lr){6-6}\cmidrule(lr){7-7}\cmidrule(lr){8-8} +% \cmidrule(lr){9-9}\cmidrule(lr){10-10}\cmidrule(lr){11-11} +% 20 & 0&5&&&&&&&&\\\cmidrule(lr){1-1} +% 10 & &10&20&30&40&50&60&70&80&90\\\cmidrule(lr){1-1} +% 9 & &11.11111&&&&&&&&100\\\cmidrule(lr){1-1} +% 8 & &12.5&&&&&&&100&\\\cmidrule(lr){1-1} +% 7 & &14.28571&&&&&&100&&\\\cmidrule(lr){1-1} +% 6 & &16.66667&&&&83.33333&100&&&\\\cmidrule(lr){1-1} +% 5 & &20&&&80&100&&&&\\\cmidrule(lr){1-1} +% 4 & &25&&75&100&&&&&\\\cmidrule(lr){1-1} +% 3 & &33.33333&66.66667&100&&&&&&\\\cmidrule(lr){1-1} +% 2 & &50&100&&&&&&&\\\cmidrule(lr){1-1} +% 1 & 0&100&&&&&&&&\\\cmidrule(lr){1-1} +% \bottomrule +% \end{tabular} +% \end{table} +% In questions where several choices can be selected (see |multi| with the option +% |multiple|), positive fractions must sum up to exactly 100. It is also possible to set +% negative fractions (from -100 to 0) for wrong choices, in order to prevent the +% selection of all choices from leading to a good grade. +% In this case, the value ranging from -100 to 0 must be the opposite of one of the +% values listed in Table~\ref{tab:fraction}. % +% \DescribeMacro{fractiontol} +% The package tries to match the |fraction| key to one of the admissible values. +% To this end, the tolerance is controlled by the |fractiontol| key. It defaults +% to |0.01| but may be changed. When no admissible fraction value is matched, the +% package throws an error. +% % \DescribeMacro{feedback} % The |feedback| key sets text that will appear to the student after completing the quiz. % For example, one might set % \begin{center} -% |feedback={A question like this will appear on the exam.}| +% |feedback={This question might show up in the final exam.}| % \end{center} % The desired feedback should be included in braces. % @@ -282,13 +452,56 @@ % question, then that feedback will appear to each student regardless of the student's answer. % Answer-specific feedback (perhaps explaining a common mistake) % may also be given by setting the |feedback| key \emph{at the individual answer}. +% +% \DescribeMacro{tags} +% The |tags| key sets a keyword for the question that will be taken into account by Moodle for +% filtering purposes or classification of questions inside the question bank. It is possible +% for instance to build a quiz with questions cherry-picked among the set of questions holding +% a particular tag. +% For example, one might set +% \begin{center} +% |tags={easy}| +% \end{center} +% The desired tag should be included in braces. +% +% Tags can be assigned at two levels. If the |tags| key is set at the quiz level, +% then that tags will be assigned by default to each question of the quiz. +% Question-specific tags can be assigned by setting the |tags| key \emph{at the question level}. +% Since only single tag is supported, the tag a the question-level overrides eventual tags +% specified at the quiz-level. % -% \newpage % \subsection{Question Types} % -% We next discuss the various question types supported by \textsf{moodle.sty} +% We next discuss the various question types supported by \textsf{moodle} % and the options that may be set. +% +%\subsubsection{True/False} +% +% \DescribeMacro{truefalse} +% The syntax for a True/False question is as follows: +% \begin{quote} +% |\begin{truefalse}|\oarg{question options}\marg{question name} \\ +% \rule{2em}{0pt}\meta{question text} \\ +% \rule{2em}{0pt}|\item*| \meta{feedback when ``true" is chosen} \\ +% \rule{2em}{0pt}|\item| \meta{feedback when ``false" is chosen} \\ +% |\end{truefalse}| +% \end{quote} +% The correct answer is designated by the asterisk |*| after the |\item|; +% it need not appear first in the list. +% +% Answer-specific feedback can also be defined as an item option, similarly to +% other types. +% \begin{quote} +% |\begin{truefalse}|\oarg{question options}\marg{question name} \\ +% \rule{2em}{0pt}\meta{question text} \\ +% \rule{2em}{0pt}|\item[feedback={|\meta{When ``true" is chosen}|}]*| \\ +% |\end{truefalse}| +% \end{quote} +% Note that, in this example, no feedback is defined for the incorrect answer +% ``False": the corresponding item can be omitted. % +% With the True/False question type, the |penalty| key has no effect. +% % \subsubsection{Multiple Choice} % % \DescribeMacro{multi} @@ -319,54 +532,66 @@ % which mirror the usual \LaTeX\ syntax: % |alph|, |Alph|, |arabic|, |roman|, |Roman|, and |none|. % Calling |numbering=none| produces an unnumbered list of answers. -% The Moodle syntax of |abc|, |ABC|, |123|, |iii|, and |IIII| is also acceptable, -% but note that it requires \emph{four} |I|'s to obtain upper-case Roman numerals this way. +% The Moodle syntax of |abc|, |ABCD|, |123|, |iii|, and |IIII| is also +% acceptable, +% but note that it requires \emph{four} capital letters to obtain +% upper-case Roman or alphabetic numerals this way. % % \DescribeMacro{fraction} % The |fraction| key can be used to designate some wrong answers % as being worth partial credit. For example, a question might read thus: -% \begin{verbatim} +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} % \begin{multi}{my question} % Compute $\int 4x^3\,dx$. % \item* $x^4+C$ % \item[fraction=50] $x^4$ % \item $12x^2$ % \end{multi} -% \end{verbatim} +% \end{VerbatimOut} +% \inputminted[gobble=2,frame=lines]{latex}{minted.doc.out} % Thus the asterisk |*| is shorthand for |fraction=100|, % whereas a bare |\item| sets |fraction=0|. % -% \DescribeMacro{single}\DescribeMacro{multiple} +% \DescribeMacro{single} % By default, the |multi| environment produces -% a multiple choice question operates with only one correct answer; +% a multiple choice question with only one correct answer; % this is called |single| mode, and on Moodle it appears with radio buttons. -% It is also possible to write questions with more than one correct answer, -% asking the user to check all correct answers. -% To do this, use the key |multiple| or |single=false|. -% +% +% \DescribeMacro{multiple} +% It is also possible to write questions with possibly more than one correct answer, +% asking the user to check all correct answers. To do this, use the key |multiple| +% or |single=false|. % The worth of each correct answers in |multiple| mode may be set by |fraction|, -% but Moodle will require that all the fractions add up to at least 100. -% If you simply designate each correct answer with |\item*|, then \textsf{moodle.sty} -% will divide the 100\% equally among those answers. -% For example, the following two examples produce equivalent results: -% \begin{verbatim} +% but Moodle asks that all the fractions add up to \emph{exactly} 100. +% If you simply designate each correct answer with |\item*|, then \textsf{moodle} +% will divide equally among those answers the points lefts for a sum of 100\%. +% Items that are not given a |fraction| are considered incorrect and selecting them +% results in negative points such that the sum of all incorrect answers is -100\%. +% For example, the following two examples are equivalent: +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} % \begin{multi}[multiple]{my question} % Which numbers are prime? +% \item[fraction=20] 2 % \item* 5 -% \item 6 % \item* 7 +% \item[fraction=-10] 1 +% \item 6 % \item 8 % \end{multi} -% \end{verbatim} -% \begin{verbatim} +% % \begin{multi}[multiple]{my question} % Which numbers are prime? -% \item[fraction=50] 5 -% \item 6 -% \item[fraction=50] 7 -% \item 8 +% \item[fraction=20] 2 +% \item[fraction=40] 5 +% \item[fraction=40] 7 +% \item[fraction=-10] 1 +% \item[fraction=-45] 6 +% \item[fraction=-45] 8 % \end{multi} -% \end{verbatim} +% \end{VerbatimOut} +% \inputminted[gobble=2,frame=lines]{latex}{minted.doc.out} +% Note that, in this example, negative fractions are set for wrong choices. This +% prevents students selecting all options to obtain a good grade with no merit. % % \subsubsection{Numerical} % @@ -387,8 +612,41 @@ % \begin{quote}\footnotesize % |\item[fraction=0,feedback={You forgot to antidifferentiate!}]| \meta{incorrect answer} % \end{quote} +% +% \DescribeMacro{tolerance} +% The |tolerance| key can be used to specify the validity of answers within some margin. +% This key can be set at different levels: quiz, question, item. +% For example, with the question +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} +% \begin{numerical}[tolerance=0.01]{my question} +% Approximate value of $\sqrt{2}$? +% \item[tolerance={1e-1}] 1.4142 +% \item[fraction=20,feedback={twice this!}] 7.0711e-1 +% \item[fraction=0,feedback={Wrong!}] * +% \end{numerical} +% \end{VerbatimOut} +% \inputminted[gobble=2,frame=lines]{latex}{minted.doc.out} +% In this example, +% \begin{itemize} +% \item any answer in the range $[1.4042,1.4242]$ will be validated, +% \item any answer in the range $[0.69711,0.71711]$ will get the specific feedback +% \emph{twice this!} and 20\% of points, +% \item any other answer is incorrect and will get the specific feedback +% \emph{Wrong!}. +% \end{itemize} +% +% When feedback is to be given for any non-specified answer, one can add a \emph{last} answer +% item containing the wilcard character |*| only. In this case, the |tolerance| key is irrelevant. +% +% Both answers and tolerance can be specified with the comma (|,|) as a decimal separator. +% Exponent notation is accepted. After import, Moodle will recognize indifferently |0.000165|, +% |0,000165|, |1.65E-4|, |1.65e-4|, |1,65E-4|, and |1,65e-4|. +% +% If the \textsf{siunitx} package is loaded, \textsf{moodle} will detect it and +% numbers will be rendered nicely in the PDF output. % -% \newpage +% Units, unit-handling and multipliers are currently unsupported. +% % \subsubsection{Short Answer} % A short answer question resembles a numerical question: the student is to fill % in a text box with a missing word or phrase. @@ -403,25 +661,40 @@ % You can make the text box appear as part of the question with the % control sequence |\blank|. For example, % your question might read -% \begin{verbatim} +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} % \begin{shortanswer}{Leibniz} % Newton's rival was Gottfried Wilhelm \blank. % \item Leibniz % \item Leibniz. % \end{shortanswer} -% \end{verbatim} +% \end{VerbatimOut} +% \inputminted[gobble=2,frame=lines]{latex}{minted.doc.out} % Note that as the blank occurred at the end of a sentence, % we included two answers, % lest students get the question wrong merely by % including or omitting a period. % % \DescribeMacro{case sensitive}\DescribeMacro{usecase} -% By default, Moodle ignores the distinction between upper- and lower-case letters -% when grading a short answer question. +% The default setting when creating a Short Answer question in Moodle +% is to ignore the distinction between upper- and lower-case letters +% when grading a short answer question. This default is preserved by +% \textsf{moodle}. % You can make a question case-sensitive with the key |case sensitive| % or its shorter synonym |usecase|. +% +% The wildcard character |*| can used to grab answers that match +% a specific pattern. For instance: +% \begin{itemize} +% \item ``\emph{Sir Isaac Newton}'', ``\emph{Isaac Newton}'' and +% ``\emph{Newton}'' will match the pattern |*Newton|, +% \item ``\emph{Gaston}'' and ``\emph{Wellington}'' will match the +% pattern |*ton|, +% \item ``\emph{Isaac}'' and ``\emph{Isaac Newton}'' will match the +% pattern |Isaac*|, +% \item any non empty answer will match the pattern |*| (wildcard alone). +% \end{itemize} % -% \subsubsection{Essay Questions} +% \subsubsection{Essay}\label{subsubsect:essay} % Instructors may ask essay questions on a Moodle quiz, % although Moodle's software is not up to the task of grading them! % Instead each essay question answer must be graded manually by the @@ -443,22 +716,23 @@ % If the |response required| key is set, Moodle will insist that the student % enter something in the blank before accepting the quiz as completed. % -% \newpage % \DescribeMacro{response format} % Moodle offers five different ways for students to enter and/or upload their % answers to an essay question. You may choose one of these five options: % \begin{description} -% \item[{\tt html}] An editor with the ability to format HTML responses +% \item[{\ttfamily html}] An editor with the ability to format HTML responses % including markup for italics, boldface, etc. This is the default. -% \item[{\tt file}] A file picker allowing the student to upload a file, such as -% a PDF or DOC file, containing the essay. -% \item[{\tt html+file}] The same HTML editor as above, but with the ability -% to upload files as well. This permits some students to type answers -% directly into the web form, and others to compose their essays in -% another program first. -% \item[{\tt text}] This editor allows only for entering plain text without any markup. -% \item[{\tt monospaced}] This yields a plain text editor, without any markup, -% and with a fixed-width font. This could be useful for entering code snippets, for example. +% \item[{\ttfamily file}] A file picker allowing the student to upload a +% file, such as a PDF or DOC file, containing the essay. +% \item[{\ttfamily html+file}] The same HTML editor as above, but with the +% ability to upload files as well. This permits some students to type +% answers directly into the web form, and others to compose their +% essays in another program first. +% \item[{\ttfamily text}] This editor allows only for entering plain text +% without any markup. +% \item[{\ttfamily monospaced}] This yields a plain text editor, without any +% markup, and with a fixed-width font. This could be useful for +% entering code snippets, for example. % \end{description} % % \DescribeMacro{response field lines} @@ -473,16 +747,16 @@ % You may also require the student to upload a certain number of attachments % by setting |attachments required| to |0|, |1|, |2|, or |3|. % -% \DescribeMacro{response template} +% \DescribeMacro{template} % Finally, you may preload the essay question with a template that the student -% will edit and/or type over, with the key |response template=|\marg{template}. +% will edit and/or type over, with the key |template=|\marg{template}. % The \meta{template} should be enclosed in braces. % -% \subsection{Matching Questions} +% \subsubsection{Matching} % % A matching question offers a series of subquestions % and a set of possible answers from which to choose. -% If there are $m$ questions and $n$ possible answers, +% If there are $m$ questions and $n\geq m$ possible answers, % a matching question will look like this: % % \begin{quote} @@ -508,19 +782,18 @@ % permute the questions and answers; by default |shuffle=true|. % % \DescribeMacro{drag and drop}\DescribeMacro{dd} -% Moodle has two formats for matching questions. -% The older matching question offered a dropdown box -% for choosing the answer to each question; -% the newer ``drag and drop matching'' format +% The standard matching question offered by Moodle corresponds to +% a dropdown box for choosing the answer to each question. +% There also exists a ``drag and drop matching'' plugin for Moodle that % shows all questions in one column, % all answers in a second column, and allows students to drag the correct % answer to the question using a mouse. -% To enable drag-and-drop matching, use the key `|drag and drop|' or `|dd|' for -% short. The default is |dd=false|. If you choose the older format, -% then due to the limitations of dropdown boxes, no \LaTeX\ or HTML code can be used -% in the answers. +% In this package, to enable drag-and-drop matching, use the key +% `|drag and drop|' or `|dd|' for short. The default is |dd=false|. +% If you choose the standard format, then due to the limitations of +% dropdown boxes, no \LaTeX\ or HTML code can be used in the answers. % -% \subsection{Cloze Questions} +% \subsubsection{Cloze Questions and Subquestions} % % A ``cloze question'' has one or more subquestions embedded within a passage of text. % For example, you might ask students to fill in several missing words within @@ -528,10 +801,10 @@ % To encode cloze questions in \LaTeX\ using this package is easy: % you simply nest one or more |multi|, |shortanswer|, or |numerical| environments % within a |cloze| environment, as in the following example: -% \begin{verbatim} -% \begin{cloze}{my cloze question} \\ +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} +% \begin{cloze}{my cloze question} % Thanks to calculus, invented by Isaac -% \begin{shortanswer} +% \begin{shortanswer}[usecase] % \item Newton % \end{shortanswer}, % we know that the derivative of $x^2$ is @@ -542,80 +815,207 @@ % \end{multi} % and that $\int_0^2 x^2\,dx$ equals % \begin{numerical} -% \item[tolerance=0.0004] 2.667 +% \item[tolerance={4e-4}] 2.667 % \end{numerical}. % Thanks, Isaac! % \end{cloze} -% \end{verbatim} +% \end{VerbatimOut} +% \inputminted[gobble=2,frame=lines]{latex}{minted.doc.out} % Note that when used as a subquestion within a cloze question, % |\begin{multi}| is \emph{not} followed by name in braces; % the same is true for the |shortanswer| and |numerical| % environments. % -% \DescribeMacro{vertical}\DescribeMacro{horizontal} -% Within a cloze question, by default a multiple choice question is implemented -% as an inline dropdown box. This is visually compact, but it also prevents the -% use of mathematical or HTML formatting. +% \DescribeMacro{single=true}\DescribeMacro{single=false}\DescribeMacro{multiple} +% Before Moodle version 3.5, within a cloze question, a multiple choice question +% was necessarily of type |single|, i.e. with a single good answer. If you intend +% to export your quiz to Moodle 3.5+, the option |multiple| can be used, when +% multiple good answers are to be found. +% +% \DescribeMacro{vertical}\DescribeMacro{horizontal}\DescribeMacro{inline} +% Within a cloze question, by default, a multiple choice question is implemented +% as an |inline| dropdown box. This is visually compact, but it also prevents +% the use of mathematical or HTML formatting. % Adding the option |vertical| displays the subquestion as a vertical column % of radio buttons instead; likewise the option |horizontal| creates a horizontal % row of radio buttons. +% The option |inline| is incompatible with |multiple| or |single=false| +% (dropdown boxes don't let you pick up several answers!). +% +% \DescribeMacro{shuffle} +% Starting from Moodle version 3.0, within a cloze question, the items of a +% multiple choice question can be shuffled. Setting |shuffle=false| will +% guarantee that the answer appear in the order they were typed; the +% default is |shuffle=true|. +% +% \DescribeMacro{case sensitive}\DescribeMacro{usecase} +% Within a cloze question, the short answer question can be made case sensitive. +% This option, disabled by default, is selected with |case sensitive| or |usecase|. +% +%\subsubsection{Description} +% +% The Moodle description type is not really a question. It is more like a label. +% One can set a |feedback| that the student gets when reviewing the submission. +% Tags can be set as well. +% +% For descriptions, \textsf{moodle} redefines the existing |description| environment. +% +% The syntax for a Description question is as follows: +% \begin{quote} +% |\begin{description}|\oarg{question options}\marg{question name} \\ +% \rule{2em}{0pt}\meta{question text} \\ +% |\end{description}| +% \end{quote} +% +% \subsection{Summary of the Key Options} +% +% Table~\ref{tab:key-options} summarizes the key options available at the question +% and answer levels depending on the question type. For the essay questions, +% please refer to section~\ref{subsubsect:essay}. +% +% \begin{table}[tbp] +% \centering +% \caption{Options offered at the question and answer levels for each question type.} +% \label{tab:key-options} +% \small +% \begin{tabular}{*{14}{l}} +% \toprule +% & \multicolumn{10}{l}{Question} & \multicolumn{3}{l}{Answer}\\ +% \cmidrule(lr){2-11}\cmidrule(lr){12-14} +% Question type & \rotatebox{90}{points} & +% \rotatebox{90}{penalty} & \rotatebox{90}{feedback} & \rotatebox{90}{tags} & +% \rotatebox{90}{shuffle} & \rotatebox{90}{numbering} & \rotatebox{90}{multiple} & +% \rotatebox{90}{usecase} & \rotatebox{90}{tolerance} &\rotatebox{90}{dd} & +% \rotatebox{90}{fraction} & \rotatebox{90}{feedback} & +% \rotatebox{90}{tolerance}\\\cmidrule(lr){1-1}\cmidrule(lr){2-2}\cmidrule(lr){3-3} +% \cmidrule(lr){4-4}\cmidrule(lr){5-5}\cmidrule(lr){6-6}\cmidrule(lr){7-7} +% \cmidrule(lr){8-8}\cmidrule(lr){9-9}\cmidrule(lr){10-10}\cmidrule(lr){11-11} +% \cmidrule(lr){12-12}\cmidrule(lr){13-13}\cmidrule(lr){14-14} +% \href{https://docs.moodle.org/35/en/Multiple_Choice_question_type} +% {Multichoice} & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ & +% $\bullet$ & $\bullet$ & $\bullet$ & & & & $\bullet$ & $\bullet$ \\ +% \href{https://docs.moodle.org/35/en/Numerical_question_type}{Numerical} +% & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ & & & +% & & $\bullet$ & & $\bullet$ & $\bullet$ & $\bullet$ \\ +% \href{https://docs.moodle.org/35/en/Short-Answer_question_type}{Short +% Answer} & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ & & & +% & $\bullet$ & & & $\bullet$ & $\bullet$ \\ +% \href{https://docs.moodle.org/35/en/Matching_question_type}{Matching} +% & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ & & +% & & & $\bullet$ & $\bullet$ & \\ +% \href{https://docs.moodle.org/35/en/True/False_question_type} +% {True/False} & $\bullet$ & & $\bullet$ & $\bullet$ & & & +% & & & & & $\bullet$ \\ +% \href{https://docs.moodle.org/35/en/Description_question_type} +% {Description} & & & $\bullet$ & $\bullet$ & & & +% & & & & & \\ +% ^^A\href{https://docs.moodle.org/35/en/Essay_question_type}{Essay} & \\\hline% +% \href{https://docs.moodle.org/35/en/Embedded_Answers_(Cloze)_question_type}{Cloze} +% & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ & & & +% & & & & & \\\cmidrule(lr){1-1} +% \hspace{1em}Numerical & $\bullet$ & & & & & & +% & &$\bullet$ & & $\bullet$ & $\bullet$ & $\bullet$ \\ +% \hspace{1em}Short Answer & $\bullet$ & & & & & & +% & $\bullet$ & & & $\bullet$ & $\bullet$ \\ +% \hspace{1em}Multi (regular) & $\bullet$ & & & & $\bullet$ & & $\bullet$ +% & & & & $\bullet$ & $\bullet$ \\ +% \hspace{1em}Multi (horizontal)& $\bullet$ & & & & $\bullet$ & & $\bullet$ +% & & & & $\bullet$ & $\bullet$ \\ +% \hspace{1em}Multi (vertical)& $\bullet$ & & & & $\bullet$ & & +% & & & & $\bullet$ & $\bullet$ \\ +% \bottomrule +% \end{tabular} +% \end{table} % % \section{Conversion to HTML} % % Questions should be typed as usual for \LaTeX, -% including |\$| to obtain dollar signs, |$|'s for math shifts, -% |$$|'s for display math, et cetera. +% including |\$| to obtain dollar signs, |$|'s or |\(|...|\)| for math shifts, +% |$$|'s or |\[|...|\]| for display math, et cetera. % The package \textsf{moodle.sty} automatically converts % this \LaTeX\ code into HTML for web display. % -% The following table lists \LaTeX\ commands and environments that -% will be converted to HTML: -% \begin{center} -% \begin{tabular}{lll} -% |~| & |\#| & |\$| \\ -% |\&| & |\\| & |\S| \\ -% |\{| & |\}| & {\tt\catcode`\ =12\relax\catcode`\\=12\relax\ }\\ -% |\emph| & |\textbf| & |\par| \\ -% |\ldots| & |\dots| & |\relax| \\ -% |\euro| & |\texteuro| \\ -% |\begin{center}| \\ -% |\begin{enumerate}| \\ -% |\begin{itemize}| \\ -% |\begin{tikzpicture}| \\ -% \end{tabular} -% \end{center} -% Single and double quotation marks and the diacritical commands |\^|, |\'|, |\`|, |\"|, |\~|, and |\H| are also handled, -% as are the characters |\aa|, |\ae|, |\o|, |\ss|, |\l|, and their capitalizations. +% Table~\ref{tab:html} lists \LaTeX\ macros, commands, and environments that +% are specifically converted to HTML. +% \begin{table}[tbp] +% \centering +% \caption{Conversion of \LaTeX\ material to HTML.} +% \label{tab:html} +% \begin{tabular}{llll} +% \toprule +% \multicolumn{2}{l}{Macros} & Commands & Environnments \\ +% \cmidrule(lr){1-2}\cmidrule(lr){3-3}\cmidrule(lr){4-4} +% |~| & |\#| &|\emph{}| & |\begin{center}|\\ +% |\$| & |\&| &|\textbf{}| & |\begin{enumerate}|\\ +% |\\| & |\par| &|\textit{}| & |\begin{itemize}|\\ +% |\&| & |\S| & |\texttt{}| & |\begin{tikzpicture}|\\ +% |\{| & |\}| & |\textsc{}| & \\ +% |\|\textvisiblespace & |\relax| & |\underline{}| &\\ +% |\,| & |\thinspace| & |\textsuperscript{}| & \\ +% |\dots| & |\ldots| & |\up{}| & \\ +% |\euro| & |\texteuro| & |\fup{}| & \\ +% |\TeX| & |\LaTeX|& |\textsubscript{}| & \\ +% |\_| & |\textbackslash| & |\url{}| & \\ +% & & |\href{}{}| & \\ +% & & |\tikz[]{}| & \\ +% & & |\includegraphics[]{}| & \\ +% & & |\verbatiminput{}| & \\ +% & & |\VerbatimInput[]{}| & \\ +% & & |\LVerbatimInput[]{}| & \\ +% & & |\BVerbatimInput[]{}| & \\ +% & & |\inputminted[]{}{}| & \\ +% \bottomrule +% \end{tabular} +% \end{table} +% Single and double quotation marks, french quotation marks, inverted exclamation and +% question marks, and the diacritical +% commands |\^|, |\'|, |\`|, |\"|, |\~|, |\c|, |\H|, |\u| and |\v| are also handled, +% as are the characters |\aa|, |\ae|, |\l|, |\oe|, |\o|, |\ss|, and their +% capitalizations. See Tables~\ref{tab:diacritical}, \ref{tab:ligatures}, and +% \ref{tab:other} for more details. % % In addition, |<| and |>| will be converted to |<| and |>| \emph{within math mode only}. % If they should be typed outside of math mode, they will be passed as typed to % the HTML, and probably interpreted by students' browsers as HTML tags % or other unpredicated results. % -% Be aware that \emph{\textsf{moodle.sty} does not know how to convert any +% Be aware that \emph{\textsf{moodle} does not know how to convert any % other \TeX\ or \LaTeX\ commands to HTML.} % If other sequences are used, they may be passed verbatim to the XML file % or may cause unpredicted results. +% \DescribeMacro{\htmlregister}The |\htmlregister| command lets you specify +% the macros that must be expanded in the XML file. It works only when no +% optional argument is used. +% +% \DescribeMacro{\moodleregisternewcommands} +% When the list of macros is long, it becomes cumbersome to record them +% individually for expansion. +% Calling |\moodleregisternewcommands| triggers the automatic +% expansion of macros defined subsequently using |\newcommand|, +% |\renewcommand|, |\providecommand|, or their starred variants. +% Again, this works only if the macros are defined \emph{without} optional +% argument. % % If you think of another \LaTeX\ command that should be changed to an HTML equivalent, -% please contact the author at \url{anders.hendrickson@snc.edu} +% please contact the maintainer at \url{guerquin-kern AT crans.org} % so that it may be added to a future revision of the package. % % \section{Graphics} -% The \textsf{moodle.sty} package can handle two kinds of graphics seamlessly. +% The \textsf{moodle} package can handle two kinds of graphics seamlessly. % External graphics files may be included with the |\includegraphics| command -% from the |graphicx| package, -% and graphics may be generated internally using \TikZ. In either case, the -% graphics will be embedded in base-64 encoding directly within +% from the |graphicx| package, and graphics may be generated internally using \TikZ. +% In either case, the graphics will be embedded in base-64 encoding directly within % the Moodle~XML produced. This prevents the hassle of managing separate % graphics files on the Moodle server, as Moodle will store the picture % within the question in the question bank. % +% \subsection{Default \texttt{includegraphics}} % \DescribeMacro{\includegraphics}\DescribeMacro{height}\DescribeMacro{width} % When using |\includegraphics|, the only options currently supported % are |height| and |width|. Attempts to use other |\includegraphics| options, % such as |scale| or |angle|, will affect the PDF but not the XML output. -% The dimensions set by |height| and |width| are \TeX\ dimensions such as |4in| or |2.3cm|. +% The dimensions set by |height| and |width| are \TeX\ dimensions such as \texttt{4\,in} +% or \texttt{2.3\,cm}. % In order to prepare the image for web viewing, this package converts those % dimensions to pixels using a default of % 103 pixels per inch.\footnote{This @@ -627,11 +1027,50 @@ % That value may be changed by setting the |ppi| key (e.g., |ppi=72|); % this is probably best done for the entire document with a |\moodleset| command, % rather than image-by-image. -% -% The mechanism used is somewhat fragile and relies upon one \TikZ\ library and three free external programs. +% \DescribeMacro{\graphicspath} +% You can use |\graphicspath{{|\emph{path}|}}| to specify a directory where the +% pictures to be included are located. +% +% A special rule was added for the inclusion of GIF pictures (|.gif| extension). +% These files are passed as-is to the XML, preserving potential animations. +% However, as pdf\TeX\ engines do not support the GIF format, the picture is +% passed to the PDF output after a conversion to the PNG format. When the GIF +% file is animated, only its first frame is passed to the PDF. +% +%^^A \DescribeMacro{\DeclareGraphicsAlien} Users may declare other graphic +%^^A formats with the command |\DeclareGraphicsAlien|\marg{alien extension} +%^^A \marg{native extension}\marg{command line for conversion}. +% +% \subsection{\TikZ\ Pictures} +% When \TikZ\ is loaded and used to define pictures, \textsf{moodle} invokes +% the |external| \TikZ\ library, so that each |tikzpicture| environment is compiled +% to a freestanding PDF file. +% +% \subsection{Package Option \texttt{tikz}}\label{subsec:tikz} +% \DescribeMacro{tikz} +% The \textsf{moodle} package admits a \texttt{tikz} option which has the following effects: +% \begin{itemize} +% \item the package \texttt{tikz} is loaded. +% \item \texttt{includegraphics} is embedded in a \TikZ\ picture. Consequences are that +% \begin{itemize} +% \item the pictures encoded in the XML file are resampled. This prevents encoding +% images at a higher resolution than rendered by Moodle. +% \item the full set of \texttt{includegraphics} options is accessible, +% e.g.~|scale=.5|, |angle=90|, or |width=.2\textwidth|. +% \end{itemize} +% \item \DescribeMacro{\embedaspict} a macro |\embedaspict{...}| is provided for the +% inclusion of inline \LaTeX\ material as images. This can serve as a workaround to +% overcome limitations of this package---like the conversion of tabulars to HTML--- +% or limitations of Moodle itself. +% For the definition of this macro, the package \texttt{varwidth} is loaded. +% \item optimizations of the \TikZ-external library are disabled. Compilation might get +% sensibly slower. +% \end{itemize} +% +% \subsection{External Tools} +% The mechanisms used for handling graphics are somewhat fragile and rely upon +% three free external programs. % \begin{enumerate} -% \item \textsf{moodle.sty} invokes |\usetikzlibrary{external}|, -% so that each |tikzpicture| environment is compiled to a freestanding PDF file. % \item GhostScript (\url{www.ghostscript.com}) is used to convert the PDF output % from \TikZ\ into a PNG raster graphics file. % The default command line is presumed to be |gswin64c.exe| @@ -639,75 +1078,459 @@ % or |gs| (if |\ifwindows| returns false). % If your system requires a different command line to invoke Ghostscript, % \DescribeMacro{\ghostscriptcommand} -% you may change it by invoking |\ghostscriptcommand|\marg{executable filename}. -% \item When external graphics files such as JPG or GIF are included, +% you may change it by invoking: +% \begin{quote} +% |\ghostscriptcommand|\marg{executable filename} +% \end{quote} +% \item When external graphics files such as PDF are included, % the open-source ImageMagick software (\url{www.imagemagick.org}) % converts each file to PNG format. % The command line for ImageMagick is the nondescript word |convert|, % \DescribeMacro{\imagemagickcommand} % but may be changed by invoking |\imagemagickcommand|\marg{executable filename}. -% \item Finally, the OpenSSL toolkit (\url{www.openssl.org}) is used to base64-encode -% the PNG file so that it can be included directly in the text of the XML file. -% The command line is presumed to be |openssl|, but can be changed with -% \DescribeMacro{\opensslcommand} -% |\opensslcommand|\marg{executable filename}. +% \item OptiPNG (\url{http://optipng.sourceforge.net/}) is used to optimize the PNG images. +% The command line is presumed to be |optipng|, but can be changed with +% \DescribeMacro{\optipngcommand} |\optipngcommand|\marg{executable filename}. % \end{enumerate} % % Please note the following vital points to make the graphics handling work: % \begin{itemize} % \item As of now, graphics are only supported when compiling directly to a PDF -% with |pdflatex|. Including PS graphics or using \TikZ\ with the DVI$\to$PS workflow is not +% with |pdflatex|. Including PS graphics or using \TikZ\ with the DVI$\to$PS workflow is not % yet supported. -% \item Filenames should not contains spaces or special characters like |_| or |\|. -% \item You must have Ghostscript, ImageMagick, and OpenSSL installed on your system -% to fully use the graphics-handling capabilities of \textsf{moodle.sty}. +%^^A \item Filenames should not contains spaces or, under windows, special characters like |_| or |\|. +% \item You must have Ghostscript and ImageMagick installed on your system +% to fully use the graphics-handling capabilities of \textsf{moodle}. +% \item If OptiPNG is not installed, the corresponding system calls will fail with otherwise no +% impact on the compilation process: PNG files are passed unoptimized to the XML output. % \item \LaTeX\ must be able to call system commands; that is, |\write18| must be enabled. % For Mik\TeX, this means adding |--enable-write18| to the command line of |pdflatex|; % for \TeX Live, this means adding |--shell-escape=true|. +% \item Due to security issues with old versions of Ghostcript, some systems default to a +% policy that prevents the conversion of PDF and PS to PNG. Assuming that, as a user of +% \textsf{moodle} which requires shell escape capabilities, you either use a sandboxed +% environment or trust the files handled at the system-level, you may want to disable this +% over-zelous security policy. For example, +% \href{https://stackoverflow.com/a/52661288/14608059}{see this}. +% \item Users of the |circuitikz| package must enclose their circuits' \TikZ\ code in the +% |tikzpicture| environment instead of |circuitikz|. That is required, +% as of \TikZ\ 2.1, by the |external| library. % \end{itemize} +% +% \subsection{Package Option \texttt{svg}}\label{subsec:svg} +% +% \paragraph{Important Notice} \emph{The \texttt{svg} option is an experimental feature +% introduced in \textsf{moodle} v0.8. It has been tested exclusively under Linux, with +% TeXLive 2020, Inkscape v1.0.1 and Scour 0.38.2.} +% +% \DescribeMacro{svg} The \textsf{moodle} package admits an experimental \texttt{svg} +% option which has the following effects: +% \begin{itemize} +% \item |\includegraphics| can be used to import SVG graphic files directly +% (extension |.svg| or |.SVG|). In this case, the SVG file is passed as-is to the +% XML output and is converted using \href{https://inkscape.org/} +% {Inkscape} (must be installed) for inclusion in the PDF output. +% \item the graphic files in PDF format are converted to the SVG format using +% \href{https://inkscape.org/}{Inkscape} (must be installed), rather than beeing +% rasterized. Before inclusion to the XML output, the SVG file is optimized using +% the \href{https://github.com/scour-project/scour}{Scour} utility. This +% optimization step is optional in the sense that, if the Scour call fails, +% the unoptimized SVG file will be passed to the XML output. +% Two processes benefit from this PDF$\to$SVG conversion: +% \begin{itemize} +% \item inclusion of PDF graphics with |\includegraphics|, and +% \item Tikz pictures that are externalized. +% \end{itemize} +% \end{itemize} +% +% \DescribeMacro{\PDFtoSVGcommand}\DescribeMacro{\SVGtoPDFcommand} +% \DescribeMacro{\optiSVGcommand} The call of external tools can be changed using +% the macros |\PDFtoSVGcommand| \marg{\dots}, |\SVGtoPDFcommand|\marg{\dots} and +% |\optiSVGcommand|\marg{\dots}. +% +% \section{Verbatim Code} +% Because, for HTML translation, \textsf{moodle} parses the body of questions, the use of +% verbatim code results in compilation errors. This is why the use of |\verb|, +% |\begin{verbatim}| and other standard utilities is not supported. +% +% However, using the following three utilities, verbatim code can be imported from an external file: +% \begin{enumerate} +% \item \DescribeMacro{\verbatiminput}|\verbatiminput|\marg{filename} from the \textsf{verbatim} +% package inserts verbatim code in both the PDF and the XML for moodle, without fancy additions. +% \item \DescribeMacro{\VerbatimInput}The macro |\VerbatimInput|\marg{options}\marg{filename} from +% \textsf{fancyvrb} or \textsf{fvextra} does more, with several options and settings offered +% (see below). +% The variants \DescribeMacro{\BVerbatimInput}|\VBerbatimInput| and +% \DescribeMacro{\LVerbatimInput}|\LVerbatimInput| are also supported, with identical +% effect on the XML output. +% The variants with a star are unsupported and result in errors when used. +% \item \DescribeMacro{\inputminted}On top of that |\inputminted|\oarg{options}\marg{lang} +% \marg{filename}from the \textsf{minted} package offers syntaxic highlighting tailored to the +% specified language. +% \end{enumerate} +% The \textsf{moodle} package handles these three commands to pass the code in the output XML. +% +% With |\VerbatimInput| and |\inputminted|, the options that are taken care of for XML +% generation are listed in Table~\ref{tab:verbatim-options}. Using |\fvset|\marg{key=value,...}, +% options can be set globally. Equivalently, with \textsf{minted}, +% |\setminted|\oarg{lang}\marg{key=value,...} is available. +% +% \begin{table}[tbp] +% \centering +% \begin{threeparttable}[b] +% \caption{Options and corresponding values considered for XML generation of verbatim material +% with \texttt{VerbatimInput} and \texttt{inputminted}.} +% \label{tab:verbatim-options} +% \begin{tabular}{ll} +% \toprule +% Option keys & Possible values\\\cmidrule(lr){1-1}\cmidrule(lr){2-2} +% ^^A\texttt{commentchar} & \meta{character}\\ +% \texttt{gobble} & \meta{integer}\\ +% \texttt{autogobble}\tnote{1} & \texttt{true} or \texttt{false}\\ +% \texttt{tabsize} & \meta{integer}\\ +% \texttt{numbers} & \texttt{none}, \texttt{left}, \texttt{right}, or \texttt{both}\tnote{2}\\ +% \texttt{firstnumber} & \texttt{auto}, \texttt{last}, or \meta{integer}\\ +% \texttt{firstline} & \meta{integer}\\ +% \texttt{lastline} & \meta{integer}\\ +% \texttt{numberblanklines} & \texttt{true} or \texttt{false}\\ +% \texttt{highlightlines}\tnote{2} & \marg{coma-separated list of integers or ranges}\\ +% \texttt{style}\tnote{1} & \meta{string}\\ +% \bottomrule +% \end{tabular} +%\begin{tablenotes} +%\item[1] \texttt{autogobble}, \texttt{numbers=both}, and \texttt{style} are offered only by \textsf{minted}. +%\item[2] line highlighting is offered only with \textsf{fvextra} or \textsf{minted} loaded. +%\end{tablenotes} +%\end{threeparttable} +%\end{table} +% +% In order to define the verbatim code from the \LaTeX\ document itself, it is still possible +% to use, outside the scope of the \textsf{moodle} questions, the environments |filecontents*| (from the +% \textsf{filecontents} package or \LaTeX\ kernel itself since 2019) or |VerbatimOut| +% (from the \textsf{fancyvrb} and \textsf{fvextra} packages). +% +% When code decorated with left-side line numbers is placed in question items, the output PDF could +% show a collision between numbers of the item and the first line. To avoid this, |\LVerbatimInput| or +% |\BVerbatimInput| can be used. Instead, when \textsf{minted} is used, the ``left-right'' mode can be +% enforced with the \LaTeX\ command: +% \begin{quote} +% |\RecustomVerbatimEnvironment{Verbatim}{LVerbatim}{}| +% \end{quote} +% +% When using utilies from \textsf{fancyvrb}, \textsf{fvextra}, or \textsf{minted}, \textsf{moodle} sets +% framing options for the display of code in the output PDF: +% \begin{quote} +% |\fvset{frame=lines,label={[Beginning of code]End of code},|\\ +% | framesep=3mm,numbersep=9pt}| +% \end{quote} +% These settings can be overidden using |\fvset| after the preamble. +% +% \section{Calculated Questions} +% Moodle's calculated questions are not supported by this package. +% +% However, as \href{https://github.com/avohns/python-latex-moodle-quiz}{demonstrated by A. +% Vohns}, an advanced scripting language may be used to generate a series of questions sharing +% the same prototype. +% +% We suggest to apply a specific tag to these questions. After import in Moodle, when creating +% a quizz, this tag can be selected to narrow down a random selection of question. +% This would mimic the behavior of calculated questions while bringing the flexibility of your +% favorite scripting language. +% +% Here are two examples inspired from the work of A. Wohns. The first one relies on the +% native Lua capabilities of \LuaLaTeX. +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} +% \begin{quiz}[tags={calculated}]{Example Quiz} +% \directlua{ +% \end{VerbatimOut} +% \inputminted[gobble=2,frame=topline]{latex}{minted.doc.out} +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} +% function clozenum_print(pair,op,result) +% tex.print("\\begin{numerical}$"..pair[1].." "..op.." "..pair[2].." +% =$".."\\item ",result,"\\end{numerical}") +% end +% function cloze_print(pair,points) +% tex.print("\\begin{cloze}[points="..points.."]{Arithmetic Quiz +% ("..pair[1]..", "..pair[2]..")}Solve the following tasks!\\\\") +% clozenum_print(pair,"+",pair[1]+pair[2]) +% clozenum_print(pair,"-",pair[1]-pair[2]) +% clozenum_print(pair,"*",pair[1]*pair[2]) +% if pair[1]/pair[2]==math.floor(pair[1]/pair[2]) then +% clozenum_print(pair,":",math.floor(pair[1]/pair[2])) +% end +% tex.print("\\end{cloze}") +% end +% for x = 2,4 do +% for y = 2,4 do +% if x>y then +% if x/y==math.floor(x/y) then points=4 else points=3 end +% cloze_print({x,y},points) +% end +% end +% end +% \end{VerbatimOut} +% \inputminted[]{lua}{minted.doc.out} +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} +% } +% \end{quiz} +% \end{VerbatimOut} +% \inputminted[gobble=2,frame=bottomline]{latex}{minted.doc.out} +% The second example makes use of the \textsf{python} package (|\usepackage{python}|). +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} +% \begin{quiz}[tags={calculated}]{Example Quiz} +% \begin{python} +% \end{VerbatimOut} +% \inputminted[gobble=2,frame=topline]{latex}{minted.doc.out} +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} +% def clozenum_print(pair,op,result): +% print(rf"""\begin{{numerical}} +% ${pair[0]} {op} {pair[1]} =$\item {result} +% \end{{numerical}}""") +% def cloze_print(pair,points): +% print(rf"""\begin{{cloze}}[points={points}]{{Arithmetic Quiz +% {(pair[0],pair[1])}}}Solve the following tasks!\\""") +% clozenum_print([x,y],"+",x+y) +% clozenum_print([x,y],"-",x-y) +% clozenum_print([x,y],"*",x*y) +% if pair[0]/pair[1] == pair[0]//pair[1]: +% clozenum_print([x,y],":",x//y) +% print("\end{cloze}") +% for x in range(2,5): +% for y in range(2,5): +% if x > y: +% if x/y == x//y: +% points=4 +% else: +% points=3 +% cloze_print([x,y],points) +% \end{VerbatimOut} +% \inputminted[]{python}{minted.doc.out} +% \begin{VerbatimOut}[gobble=1]{minted.doc.out} +% \end{python} +% \end{quiz} +% \end{VerbatimOut} +% \inputminted[gobble=2,frame=bottomline]{latex}{minted.doc.out} +% These two codes yield the same XML content. % -% \newpage % \section{Known Limitations and Call for Bug Reports} -% Some features of Moodle quizzes have not yet been implemented in \textsf{moodle.sty}. +% Table~\ref{tab:support-limitations} lists some different features supported, limitations, and +% bugs. +% +%\begin{table}[tbp] +%\centering +%\begin{threeparttable}[b] +%\caption{Content enrichment (pictures, equations) support after XML import in Moodle v3.5.7, +% depending on the question type.} +%\label{tab:support-limitations} +%\begin{tabular}{lccc} +% \toprule +% & \multicolumn{3}{l}{XML rendering in\dots}\\\cmidrule(lr){2-4} +%Question type & Question & Answer & Feedback\\\cmidrule(lr){1-1}\cmidrule(lr){2-2} +%\cmidrule(lr){3-3}\cmidrule(lr){4-4} +% \href{https://docs.moodle.org/35/en/Multiple_Choice_question_type}{Multichoice} +%& yes & yes & yes \\ +% \href{https://docs.moodle.org/35/en/Numerical_question_type}{Numerical} +%& yes & no\tnote{1} & yes \\ +% \href{https://docs.moodle.org/35/en/Short-Answer_question_type}{Short Answer} +% & yes & no\tnote{1} & yes \\ +% Matching (\href{https://docs.moodle.org/35/en/Matching_question_type}{std}) +%& yes & no\tnote{2} & no\tnote{3} \\ +% Matching (\href{https://docs.moodle.org/35/en/Drag_and_drop_matching_question_type}{dd}) +% & yes & yes\tnote{4} & no\tnote{3} \\ +% \href{https://docs.moodle.org/35/en/Essay_question_type}{Essay} +%& yes & yes\tnote{5,6} & yes\tnote{5} \\ +% \href{https://docs.moodle.org/35/en/True/False_question_type}{True/False} +%& yes & no & yes \\ +% \href{https://docs.moodle.org/35/en/Description_question_type}{Description} +%& yes & $\varnothing$ & yes \\ +%\href{https://docs.moodle.org/35/en/Embedded_Answers_(Cloze)_question_type}{Cloze} & +%yes & $\varnothing$& $\varnothing$ \\\cmidrule(lr){1-1} +%\hspace{1em}Numerical & yes & no\tnote{1} & yes \\ +%\hspace{1em}Short Answer & yes & no\tnote{1} & yes\tnote{7} \\ +%\hspace{1em}Multi (regular) & yes & no\tnote{2} & yes\tnote{7} \\ +%\hspace{1em}Multi (horizontal) & yes & yes & yes \\ +%\hspace{1em}Multi (vertical) & yes & yes & yes \\\bottomrule +%\end{tabular} +%\begin{tablenotes} +%\item[1] Moodle prompts the student for an answer and then compares it to the +%solutions provided. This is text-only. +%\item[2] Moodle uses a dropdown list to let one choose among the possible +%answers. This forbids either picture inclusion and \LaTeX\ rendering. +%\item[3] Not supported by Moodle (in this context, answer-specific feedback +%represents lots of possible combinations). +%\item[4] The drag-and-drop-matching plugin seems broken before version 1.6 +%20190409. Moodle's XML import fails with a \textsf{dmlwriteexception} when +%the field content exceeds few hundreds characters. This prevents the inclusion +%of most base64 images and maybe some complicated equations. +%\item[5] For this question type and in the context of XML generation, the +%Answer column represents the ``template" while the Feedback column represents +%the ``notes for the grader". Obviously, the grading process is not automatic +%and there is no answer-specific feedback. +%\item[6] Picture and \LaTeX\ rendering could be done, but only after +%submission and only if the keyval ``response format" is set to ``html". +%\item[7] Moodle only reveals the feedback when hovering the checkmark or X +%mark with the mouse. +%\end{tablenotes} +%\end{threeparttable} +%\end{table} +% +% Tables~\ref{tab:diacritical}, \ref{tab:ligatures}, and \ref{tab:other} describe the current +% support for some special characters, accents and other diacritical marks. +% +%\begin{table}[tbp] +% \centering +% \caption{Support for diacritical marks +% in a UTF8-coded \TeX~document compiled with (pdf)\LaTeX\ (packages +% \texttt{inputenc} with option \texttt{utf8} and \texttt{fontenc} with option \texttt{T1}), +% \LuaLaTeX\ or \XeLaTeX\ (package \texttt{fontspec}).} +% \label{tab:diacritical} +% \begin{threeparttable}[t] +% \begin{tabular}{*{6}{l}} +% \toprule +% \multicolumn{4}{l}{Input type} & \multicolumn{2}{l}{Engine support}\\ +% \cmidrule(lr){1-4}\cmidrule(lr){5-6} +% \multicolumn{2}{l}{Unicode} & \multicolumn{2}{l}{\LaTeX} & \XeLaTeX\ or \LuaLaTeX & (pdf)\LaTeX \\ +% \cmidrule(lr){1-2}\cmidrule(lr){3-4}\cmidrule(lr){5-5}\cmidrule(lr){6-6} +% \aa & \AA & \verb|\aa| & \verb|\AA| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \`a & \`A & \verb|\`a| & \verb|\`A| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \^a & \^A & \verb|\^a| & \verb|\^A| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \~a & \~A & \verb|\~a| & \verb|\~A| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \'e & \'E & \verb|\'e| & \verb|\'E| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \`e & \`E & \verb|\`e| & \verb|\`E| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \"e & \"E & \verb|\"e| & \verb|\"E| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \^e & \^E & \verb|\^e| & \verb|\^E| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \^i & \^I & \verb|\^i| & \verb|\^I| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \"i & \"I & \verb|\"i| & \verb|\"I| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \~n & \~N & \verb|\~n| & \verb|\~N| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \~o & \~O & \verb|\~o| & \verb|\~O| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \"o & \"O & \verb|\"o| & \verb|\"O| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \^o & \^O & \verb|\^o| & \verb|\^O| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \`u & \`U & \verb|\`u| & \verb|\`U| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \"u & \"U & \verb|\"u| & \verb|\"U| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \^u & \^U & \verb|\^u| & \verb|\^U| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \c{c} & \c{C} & \verb|\c{c}| & \verb|\c{C}| & Unicode and \LaTeX & Unicode and \LaTeX \\ +% \c{s} & \c{S} & \verb|\c{s}| & \verb|\c{S}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \c{t} & \c{T} & \verb|\c{t}| & \verb|\c{T}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \H{o} & \H{O} & \verb|\H{o}| & \verb|\H{O}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \H{u} & \H{U} & \verb|\H{u}| & \verb|\H{U}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \"y & \"Y & \verb|\"y| & \verb|\"Y| & Unicode and \LaTeX & \LaTeX only\\ +% \u{a} & \u{A} & \verb|\u{a}| & \verb|\u{A}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \u{e} & \u{E} & \verb|\u{e}| & \verb|\u{E}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \u{g} & \u{G} & \verb|\u{g}| & \verb|\u{G}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \u{\i} & \u{I} & \verb|\u{\i}| & \verb|\u{I}| & Unicode and \LaTeX & \LaTeX\ only\tnote{1}\\ +% \u{o} & \u{O} & \verb|\u{o}| & \verb|\u{O}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \v{c} & \v{C} & \verb|\v{c}| & \verb|\v{C}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \v{d} & \v{D} & \verb|\v{d}| & \verb|\v{D}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \v{e} & \v{E} & \verb|\v{e}| & \verb|\v{E}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \v{l} & \v{L} & \verb|\v{l}| & \verb|\v{L}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \v{n} & \v{N} & \verb|\v{n}| & \verb|\v{N}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \v{r} & \v{R} & \verb|\v{r}| & \verb|\v{R}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \v{s} & \v{S} & \verb|\v{s}| & \verb|\v{S}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \v{t} & \v{T} & \verb|\v{t}| & \verb|\v{T}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \v{z} & \v{Z} & \verb|\v{z}| & \verb|\v{Z}| & Unicode and \LaTeX & \LaTeX\ only\\ +% \bottomrule +% \end{tabular} +% \begin{tablenotes} +% \item[1] \XeLaTeX\ renders correctly |\u{i}|, that is, without a superscript dot. +% Instead, with (pdf)\LaTeX\ the rendering of |\u{i}| is flawed by the +% superposition of the superscript dot and the breve diacritical mark. +% Both engines render |\u{\i}| as expected. +% \end{tablenotes} +% \end{threeparttable} +% \end{table} +% +%\begin{table}[tbp] +% \centering +% \begin{threeparttable}[t] +% \caption{Support for ligatures in a UTF8-coded \TeX~document compiled with (pdf)\LaTeX\ (packages +% \texttt{inputenc} with option \texttt{utf8}, \texttt{fontenc} with option \texttt{T1}), +% \LuaLaTeX\ or \XeLaTeX\ (package \texttt{fontspec}).} +% \label{tab:ligatures} +% \begin{tabular}{llllll} +% \toprule +% \multicolumn{4}{l}{Input type} & \multicolumn{2}{l}{Engine support}\\ +% \cmidrule(lr){1-4}\cmidrule(lr){5-6} +% \multicolumn{2}{l}{Unicode} & \multicolumn{2}{l}{\LaTeX} & \XeLaTeX \ or \LuaLaTeX& (pdf)\LaTeX \\ +% \cmidrule(lr){1-2}\cmidrule(lr){3-4}\cmidrule(lr){5-6}\cmidrule(lr){6-6} +% \ae & \AE & \verb|\ae| & \verb|\AE| & Unicode and \LaTeX & \LaTeX\ only\\ +% \oe & \OE & \verb|\oe| & \verb|\OE| & Unicode and \LaTeX & \LaTeX\ only\\ +% \ss & \SS & \verb|\ss| & \verb|\SS| & Unicode and \LaTeX\tnote{1} & \LaTeX\ only\tnote{2}\\ +% \bottomrule +% \end{tabular} +% \begin{tablenotes}[b] +% \item[1] the Libertine font, used in this documentation and available for instance via the +% package |libertine|, defines the glyph \SS. Most fonts do not define this glyph. +% \item[2] \LaTeX\ defines the \verb|\SS| macro but (pdf)\LaTeX\ renders it as a doubled capital S. +% \end{tablenotes} +% \end{threeparttable} +% \end{table} +% +%\begin{table}[tbp] +% \centering +% \begin{threeparttable}[b] +% \caption{Support for other glyphs and punctuation marks in a UTF8-coded \TeX~document compiled +% with (pdf)\LaTeX\ (packages \texttt{inputenc} with option \texttt{utf8}, \texttt{fontenc} with +% option \texttt{T1}), \LuaLaTeX\ or \XeLaTeX\ (package \texttt{fontspec}).} +% \label{tab:other} +% \begin{tabular}{llllll} +% \toprule +% \multicolumn{4}{l}{Input type} & \multicolumn{2}{l}{Engine support}\\ +% \cmidrule(lr){1-4}\cmidrule(lr){5-6} +% \multicolumn{2}{l}{Unicode} & \multicolumn{2}{l}{\LaTeX} & \XeLaTeX\ or \LuaLaTeX & (pdf)\LaTeX \\ +% \cmidrule(lr){1-2}\cmidrule(lr){3-4}\cmidrule(lr){5-5}\cmidrule(lr){6-6} +% \l & \L & \verb|\l| & \verb|\L| & Unicode and \LaTeX & \LaTeX\ only\\ +% \o & \O & \verb|\o| & \verb|\O| & Unicode and \LaTeX & \LaTeX\ only\\ +% « & & \verb|\guillemotleft|\tnote{1} & & Unicode and \LaTeX & \LaTeX\ only\\ +% » & & \verb|\guillemotright|\tnote{1} & & Unicode and \LaTeX & \LaTeX\ only\\ +% ¿ & & \verb|\textquestiondown| & & Unicode and \LaTeX & \LaTeX\ only\\ +% ¡ & & \verb|\textexclamdown| & & Unicode and \LaTeX & \LaTeX\ only\\ +% \bottomrule +% \end{tabular} +% \begin{tablenotes} +% \item[1] the package |babel| loaded with option |french| defines |\og| and |\fg| for +% the same symbols. These are also supported by \textsf{moodle}. +% \end{tablenotes} +% \end{threeparttable} +% \end{table} +% Some features of Moodle quizzes have not yet been implemented in \textsf{moodle}. % Here is a non-exhaustive list. % \begin{itemize} % \item Moodle's feature of designating feedback for correct, % partially correct, and incorrect answers. -% \item Calculated questions; that is, automatically generated numerical questions -% using randomly chosen numbers. +%^^A \item Calculated questions; that is, automatically generated numerical questions +%^^A using randomly chosen numbers. % \item Hints -% \item Keywords -% \item So-called ``description'' questions. -% \item Shuffled answers in multiple choice cloze subquestions. +% \item Multiple keywords (tags) for questions % \end{itemize} -% I have used Version 0.5 for one semester's teaching, -% but if other users adopt this package, I fully expect them to find bugs. -% \emph{Please} send all bugs you find to \url{anders.hendrickson@snc.edu}, -% so that I can fix them for subsequent versions. +% The authors have used this package together with a limited number of colleagues +% for a few semesters of teaching. If other users adopt this package, we fully expect them +% to find bugs. \emph{Please} send all bugs you find to \url{guerquin-kern@crans.org}, +% so that we can fix them for subsequent versions. % % \section{Compatibility} -% This package has been written for and tested with the implementation of +% This package has been originally written for and tested with the implementation of % Moodle 2.9 run by Moodlerooms for St.~Norbert College in January 2016. -% Future versions of this package will probably include some support for -% specifying your version of Moodle in the |.tex| file to help ensure compatability. +% Since then, it has been successfully combined with Moodle 3.5. +% Future versions of this package might include some support for +% specifying your version of Moodle in the |.tex| file to help ensure compatibility. % % As the ultimate purpose of this package is the generation of XML files, -% future versions of \textsf{moodle.sty} will attempt to maintain backwards -% compatability with earlier versions of regarding the XML output, apart from +% future versions of \textsf{moodle} will attempt to maintain backwards +% compatability with earlier versions regarding the XML output, apart from % bug fixes. % Backwards compatibility of the PDF output is not yet guaranteed, however, % in case the author or users discover better ways for the PDF to display % the underlying XML data to be proofread. % % In other words, compiling your current |.tex| file with a future version -% of \textsf{moodle.sty} should produce the same XML file it does now +% of \textsf{moodle} should produce the same XML file it does now % (apart from bug fixes), % but it might produce a more informative, and hence different, % PDF output. % % % \section{Unrelated Tip: Quality of Moodle \TeX\ Images} -% This has nothing to do with |moodle.sty|, but is a Frequently Asked Question +% This has nothing to do with \textsf{moodle}, but is a Frequently Asked Question % in is own right. % On some servers, at least, Moodle's default ``\TeX\ Filter'' for % displaying mathematical notation is of abysmally poor quality, rending mathematics @@ -716,40 +1539,201 @@ % but turn ``MathJax'' \emph{on}. This forces \TeX\ code to be rendered by MathJax % instead of Moodle, producing much higher-quality results. % +% ^^A\clearpage +% \changes{v0.5}{2016/01/05}{Initial version} +% \changes{v0.6}{2019/02/18}{Bux-fixing release} +% \changes{v0.7}{2020/07/09}{Feature extensions} +% \changes{v0.8}{2021/01/04}{Bug fixes and feature extensions} +% \begin{changelog}[title={Version History},author={Matthieu Guerquin-Kern}] +% \begin{version}[version=0.8,date=2021-01-04] +% \added +% \item Support for inclusion of GIF pictures. +% \item Added package option |svg| to avoid the rasterization of vector graphics. +% \item New macro |\setsubcategory| to define subcategories, reflected in PDF and XML. +% \item Package option |handout| for sharing PDF with students. +% \item Extensions can be omitted when including pictures. +% \item Description question type. +% \item \LuaLaTeX\ is now supported (and recommended for UTF8-coded sources). +% \item Examples of ways to reproduce the behavior of calculated questions. +% \item Command to trigger the automatic recording of new commands. +% \item Mechanism to match |fraction| key to values accepted by Moodle. +% \item A |fractiontol| key to control the tolerance in this mechanism. +% \item Support for inverted punctuation marks |¿| and |¡|. +% \item Support for |\_| and |\textbackslash|. +% \item Support for the wildcard character as an answer in numerical questions. +% \changed +% \item Template of Essay questions is now shown in PDF. +% \item The macro |\setcategory| is reflected by a new section in PDF. +% \item In matching questions, warnings are raised if the number of items is insufficient. +% \item Improved display of matching questions in PDF. +% \item The package \textsf{iftex} is now required. +% \item An error is thrown when |fraction| is set to an invalid value. +% \item In numerical questions, the tolerance can be set in exponent form. +% \item Nicer PDF rendering of numbers in numerical questions if \textsf{siunitx} is loaded. +% \item Included PNG and JPEG files are now directly converted to base64. +% \fixed +% \item TeX's inline math (|$...$|) can now contain escaped dollar signs (|\$|). +% \item Closing braces escaped in cloze subquestions outside math environment. +% \item The scope of the |tolerance| key is now respected. +% \end{version} +% \begin{version}[version=0.7,date=2020-09-06] +% \added +% \item Support for inclusion of verbatim code. +% \item Package option \texttt{tikz}. +% \item Support for |\"Y| and |\"y|. +% \item New commands converted to XML. +% \item Adding a stamp comment in XML, package option offered to disable +% this behavior. +% \item Support for the |\tikz| command. +% \item A different directory can be specified for picture inclusion. +% \item Warn user of the \texttt{babel} package set for french that autospacing +% must be deactivated. +% \item Square bracket math delimiters are recognized and converted properly. +% \item Support of breve and caron diacritical marks. +% \changed +% \item In multi with multiple answers allowed, choosing all options no longer +% results in a good grade. An automatic penalty mechanism is introduced. +% Can be overridden by manually setting fractions. +% \removed +% \item Irrelevant \texttt{penalty} tag in cloze subquestions. +% \fixed +% \item Non-integer fractions can now be specified in cloze subquestions. +% \item Signifiantly squeeze PNG images size by skipping ancillary data. +% \item Enumerate or itemize environment can now be nested in question items. +% \item Several pictures can be included in a question without being mixed +% in the XML file. +% \item management and rendering of fraction in questions. +% \item Correctly handling a \LaTeX\ starting the last item of a question. +% \item Closing braces escaped in cloze subquestions. This allows \LaTeX\ +% equations or images to be included. +% \item Image inclusion with MacOS. +% \end{version} +% \begin{version}[version=0.6b,date=2019-11-27] +% \added +% \item New package options to set section or subsection at the quiz level. +% \item True/False question type is now supported. +% \item Moodle tags can now be specified for questions (and rendered in PDF +% as well). +% \item In cloze questions, the \texttt{multiresponse} subquestion type is +% now supported. +% \removed +% \item External dependency on \texttt{OpenSSL}. +% \item Irrelevant tags were written in XML for matching questions. +% \fixed +% \item \TikZ\ externalization now works when using \XeLaTeX. +% \item It is now possible to set points manually among several correct +% answers in multichoice questions. +% \item General feedbacks can now contain backslashes. +% \item Several quizzes can now be defined in a single source file, each +% specifying a category for Moodle's question bank. +% \item Correct encoding information in now written in XML depending on +% the \LaTeX\ compiler used. +% \end{version} +% \begin{version}[version=0.6a,date=2019-06-21] +% \added +% \item \XeLaTeX\ is now recommended when using UTF8-encoded sources (support +% of accents). +% \item Feedbacks are now displayed in the PDF file produced. +% \item The \texttt{optipng} utility is used (and required) to reduce the size +% of images embedded in the XML file. +% \item Question options and settings are now displayed in the PDF file +% \item Supporting more \LaTeX\ macros for symbols and accents (mostly +% diacritical marks and ligatures). +% \item Introduce shuffle options in cloze-multi subquestions. +% \item Package option \texttt{final}. +% \changed +% \item In draft mode, \TikZ\ externalization in no longer triggered. +% \fixed +% \item In the different question types, the feedback fields are now converted +% for HTML allowing \LaTeX\ equation and images. +% \item Documentation improvements (limitations and previously undocumented +% features). +% \end{version} +% \shortversion{version=0.5,date=2016-01-05,simple,changes=Initial version,author={Anders O.F. Hendrickson}} +% \end{changelog} +% % \StopEventually{} % % \section{Implementation} % \subsection{Packages, Options, and Utilities} % \begin{macrocode} \newif\ifmoodle@draftmode +\newif\ifmoodle@handout +\newif\ifmoodle@stampmode +\newif\ifmoodle@tikz \newif\ifmoodle@tikzloaded +\newif\ifmoodle@svg +\newif\ifmoodle@section +\newif\ifmoodle@subsection +\newif\ifmoodle@numbered %%DECLARATION OF OPTIONS \DeclareOption{draft}{\moodle@draftmodetrue} +\DeclareOption{handout}{\moodle@handouttrue} +\DeclareOption{final}{\moodle@draftmodefalse} +\DeclareOption{nostamp}{\moodle@stampmodefalse} +\DeclareOption{tikz}{\moodle@tikztrue} +\DeclareOption{svg}{\moodle@svgtrue} +\DeclareOption{section}{\moodle@sectiontrue\moodle@numberedtrue} +\DeclareOption{section*}{\moodle@sectiontrue\moodle@numberedfalse} +\DeclareOption{subsection}{\moodle@sectionfalse\moodle@numberedtrue} +\DeclareOption{subsection*}{\moodle@sectionfalse\moodle@numberedfalse} \moodle@draftmodefalse +\moodle@handoutfalse +\moodle@stampmodetrue +\moodle@tikzfalse \moodle@tikzloadedfalse +\moodle@svgfalse +\moodle@subsectiontrue +\moodle@numberedfalse \ProcessOptions \RequirePackage{environ} %To be able to take environment body as a macro argument \RequirePackage{xkeyval} %For key-handling \RequirePackage{amssymb} %For \checkmark symbol -\RequirePackage{trimspaces} %To remove extra spaces from strings +%\RequirePackage{trimspaces} %To remove extra spaces from strings (loaded by environ) \RequirePackage{etex} %Expansion control, detokenization, etc. \RequirePackage{etoolbox}%List management \RequirePackage{xpatch} %To patch commands easily in HTML mode \RequirePackage{array} %For formatting tables in the LaTeX mode of Clozes \RequirePackage{ifplatform} % To choose Ghostscript commands -\RequirePackage{ifpdf} % Needed to know whether we can convert output from PDF to PNG - +\@ifpackageloaded{iftex}{}{\RequirePackage{iftex}} +% iftex already required by recent versions of ifplatform. Needed to know: +% 1) whether we can convert output from PDF to PNG (ifpdf), +% 2) when output pdf is latin1-encoded (ifpdf) +% 3) when output xml is utf8-encoded (if?tex) +\@ifundefined{ifpdf}{\RequirePackage{ifpdf}}{}% old iftex would not define the conditional +\RequirePackage{shellesc} %Luatex-compatible way of getting system access +\RequirePackage{fancybox} %For fancy LaTeX tags \RequirePackage{getitems} %To gather the header and items +\ifmoodle@handout + \RequirePackage[seed=42]{randomlist} %To randomize answers in matching questions +\fi +\ifmoodle@svg + \RequirePackage{graphicx} %To include graphics +\fi \let\xa=\expandafter \def\@star{*}% \def\@hundred{100}% +\def\@fifty{50}% \def\@moodle@empty{}% \def\@relax{\relax}% +\def\@moodle@par{\par}% + +% Taken from https://tex.stackexchange.com/questions/47576/combining-ifxetex-and-ifluatex-with-the-logical-or-operation +\@ifundefined{ifTUTeX}{ + \newif\ifTUTeX % a new conditional starts as false + \ifnum 0\ifXeTeX 1\fi\ifLuaTeX 1\fi>0% + \TUTeXtrue + \fi +}{}% +\newif\ifpdfoutput % a new conditional starts as false +\ifnum 0\ifTUTeX 1\fi\ifPDFTeX\ifpdf 1\fi\fi>0% + \pdfoutputtrue +\fi % \end{macrocode} % As the package involves a fair bit of file processing, % we automate the naming of auxiliary files. @@ -787,9 +1771,42 @@ \newwrite\moodle@outfile \def\openmoodleout{% \immediate\openout\moodle@outfile=\outputfilename\relax - \writetomoodle{<?xml version="1.0" encoding="UTF-8"?>}% + \ifPDFTeX % latin1-based engines (pdflatex or latex) + \writetomoodle{<?xml version="1.0" encoding="iso-8859-1"?>}% + \else + \ifTUTeX % UTF8-based engines (XeTeX or LuaTeX) + \writetomoodle{<?xml version="1.0" encoding="UTF-8"?>}% + \else % what shall we do? + \writetomoodle{<?xml version="1.0" encoding="UTF-8"?>}% + %\stop + \fi + \fi + \ifmoodle@stampmode + \def\moodle@stamp{This file was generated on \the\year-\two@digits\month-\two@digits\day} + \ifPDFTeX % pdflatex or latex + \ifpdf % pdflatex + \g@addto@macro{\moodle@stamp}{ by pdfLaTeX }% + \else % latex + \g@addto@macro{\moodle@stamp}{ by LaTeX }% + \fi + \else + \ifXeTeX % xetex + \g@addto@macro{\moodle@stamp}{ by XeLaTeX }% + \else + \ifLuaTeX % luatex + \g@addto@macro{\moodle@stamp}{ by LuaLaTeX }% + \else + \g@addto@macro{\moodle@stamp}{ a TeX engine }% + \fi + \fi + \fi + \writetomoodle{<!-- \moodle@stamp -->}% + \def\moodle@stamp{running on \platformname}% + \g@addto@macro{\moodle@stamp}{ with the package moodle v0.8 }% + \writetomoodle{<!-- \moodle@stamp -->}% + \fi + \immediate\write\moodle@outfile{}% \writetomoodle{<quiz>}% - \writetomoodle{ }% }% \def\closemoodleout{% \writetomoodle{ }% @@ -797,6 +1814,7 @@ \immediate\closeout\moodle@outfile }% % \end{macrocode} +% % To both make this |.sty| file and the XML output more readable, % we create a mechanism for writing to the output file with indents. % The macro |\calculateindent|\marg{$n$} globally defines @@ -836,27 +1854,71 @@ % We now create the main |quiz| environment that will contain % the questions we write. It outputs to XML as a |<question type="category">| tag. % \begin{macrocode} -\newenvironment{quiz}[2][]% - {\setkeys{moodle}{#1}% - \@moodle@ifgeneratexml{% - \openmoodleout% - \setcategory{#2}% - }{}% - \subsection*{#2}% - \begin{enumerate}% - } - {\end{enumerate}% - \@moodle@ifgeneratexml{\closemoodleout}{}} - -{\catcode`\$=12\catcode`\ =12% -\gdef\setcategory#1{% +{\catcode`\$=12\catcode`\ =12% in this context we cannot indent with spaces... +\gdef\moodle@write@category@xml#1{% +\@moodle@ifgeneratexml{% +\writetomoodle{ }% \writetomoodle{<question type="category">}% \writetomoodle{ <category>}% -\writetomoodle{ <text>$module$/#1</text>}% +\writetomoodle{ <text>$course$/top/#1</text>}% \writetomoodle{ </category>}% \writetomoodle{</question>}% \writetomoodle{ }% +}{}% }}% +\newcommand*\@enumeratename{enumerate}% +\newenvironment{quiz}[2][]{% + \setkeys{moodle}{#1}% + \gdef\setcategory##1{% + % At first call (end of \begin{quiz}) enumerate is not started yet + \ifx\@currenvir\@enumeratename + % In case no question is defined between two calls of \setcategory + \def\@noitemerr{\@latex@warning{Empty question list}}% + \end{enumerate}% + \fi + \gdef\moodle@currentcategory{##1}% + \moodle@write@category@xml{##1}% + \ifmoodle@section + \ifmoodle@numbered + \section{##1}% + \else + \section*{##1}% + \fi + \else + \ifmoodle@numbered + \subsection{##1}% + \else + \subsection*{##1}% + \fi + \fi + \begin{enumerate}% + }% + \gdef\setsubcategory##1{% + \def\@noitemerr{\@latex@warning{Empty question list}}% + \end{enumerate}% + \moodle@write@category@xml{\moodle@currentcategory/##1}% + \ifmoodle@section + \ifmoodle@numbered + \subsection{##1}% + \else + \subsection*{##1}% + \fi + \else + \ifmoodle@numbered + \subsubsection{##1}% + \else + \subsubsection*{##1}% + \fi + \fi + \begin{enumerate}% + }% + \setcategory{#2}% +}{% + \end{enumerate}% + \let\setcategory\relax + \let\setsubcategory\relax +}% + % \end{macrocode} % The next utility takes a single macro control sequence |#1|, % and allows that macro's current value to persist @@ -888,6 +1950,19 @@ }% } % \end{macrocode} +% Now the macros |openmoodleout| and |closemoodleout| are triggered at Begin and End Document, respectively +% \begin{macrocode} +\AfterEndPreamble{ + \@moodle@ifgeneratexml{% + \openmoodleout% + }{}% +} +\AtEndDocument{ + \@moodle@ifgeneratexml{% + \closemoodleout% + }{}% +} +% \end{macrocode} % % \subsection{Key-Value Pairs for Quiz Questions} % @@ -902,7 +1977,7 @@ % to the output XML file. % % The ordinary version |\generate@moodle@write@code| passes its output text |#3| -% through the HTMLizer, producing HTML code, while the starred version +% through the HTMLizer, producing HTML code, while the starred variant % |\generate@moodle@write@code*| passes |#3| verbatim as text. % % For example, @@ -951,6 +2026,23 @@ }% }% +\def\generate@moodle@write@html@noptag#1<#2>#3{% + % No <P>..</P> introduced + % #1 = NAME for \moodle@writeNAME + % #2 = HTML tag + % #3 = what, exactly, to write + \xa\gdef\csname moodle@write#1\endcsname{% + \xa\def\xa\test@iii\xa{#3}% + \ifx\test@iii\@moodle@empty + \writetomoodle[2]{ <#2 format="html"><text/></#2>}% + \else + \xa\converttohtmlmacro\xa\moodle@htmltowrite\xa{#3}% + \writetomoodle[2]{ <#2 format="html">}% + \writetomoodle[4]{ <text><![CDATA[\moodle@htmltowrite]]></text>}% + \writetomoodle[2]{ </#2>}% + \fi + }% +}% \def\generate@moodle@write@data*#1<#2>#3{% % #1 = NAME for \moodle@writeNAME @@ -961,16 +2053,33 @@ }% }% +\def\generate@moodle@write@tags#1{% + % #1 = NAME for \moodle@writeNAME + % #3 = what, exactly, to write + \xa\gdef\csname moodle@writetags\endcsname{% + %\xa\xa\xdef\xa\xa\ds\xa\xa{Encountered '\string #1'}\show\ds + \xa\xdef\xa\test@iii\xa{\moodle@tags}% + \ifx\test@iii\@moodle@empty\relax\else + \xa\converttohtmlmacro\xa\moodle@htmltowrite\xa{\moodle@tags}% + \writetomoodle[2]{ <tags>}% + \writetomoodle[4]{ <tag><text><![CDATA[\moodle@htmltowrite]]></text></tag>}% + \writetomoodle[2]{ </tags>}% + \fi + }% +}% + % \end{macrocode} % \subsubsection{Keys for all question types} % \begin{macrocode} %% QUESTIONNAME \define@cmdkey{moodle}[moodle@]{questionname}{}% - \gdef\moodle@writequestionname{% - \writetomoodle[2]{<name>}% - \writetomoodle[4]{ <text>\moodle@questionname</text>}% - \writetomoodle[2]{</name>}% - }% +% \gdef\moodle@writequestionname{% +% \writetomoodle[2]{<name>}% +% \writetomoodle[4]{ <text>\moodle@questionname</text>}% +% \writetomoodle[2]{</name>}% +% }% +%\generate@moodle@write@code{questionname}<name>{\moodle@questionname}% +\generate@moodle@write@html@noptag{questionname}<name>{\moodle@questionname}% %% QUESTIONTEXT %I tried to use questiontext as a key, but it doesn't seem to work. @@ -986,9 +2095,9 @@ \generate@moodle@write@code{questiontext}<questiontext>{\moodle@questiontext}%{% %% PENALTY FOR WRONG ATTEMPT - \define@cmdkey{moodle}[moodle@]{penalty}[0.1000000]{}% + \define@cmdkey{moodle}[moodle@]{penalty}[0.10]{}% \generate@moodle@write@code*{penalty}<penalty>{\moodle@penalty}% - \moodleset{penalty}% + \moodleset{penalty=0.10}% %% FEEDBACK % Moodle allows for feedback tailored to each question, @@ -1002,7 +2111,7 @@ \define@cmdkey{moodle}[moodle@]{feedback}[]{}% \generate@moodle@write@code{generalfeedback}<generalfeedback>{\moodle@feedback}% \generate@moodle@write@code{feedback}<feedback>{\moodle@feedback}% - \moodleset{feedback}% + \moodleset{feedback={}}% %% DEFAULT GRADE %The next line creates \moodle@defaultgrade, @@ -1028,7 +2137,14 @@ \moodle@writehidden% }% -% TO DO: 'tags' (i.e., keywords) +%% TAGS + %The next line creates \moodle@tags, + %which defines a "tag" (i.e., keyword) for the question. + %Key calls like [tags={random}] set \moodle@tags. + \define@cmdkey{moodle}[moodle@]{tags}[]{}% + \generate@moodle@write@tags{\csname moodle@tags\endcsname}% + \moodleset{tags}% +% TODO: handle multiple 'tags' for one question % \end{macrocode} % \subsubsection{Keys for all answers} @@ -1039,13 +2155,18 @@ %the XML within the answer tag, like <answer fraction="75">. \moodleset{fraction=100} %This sets the default. % \end{macrocode} +% \begin{macrocode} +%% FRACTIONTOL -- the tolerance for fractions with respect to valid values + \define@cmdkey{moodle}[moodle@]{fractiontol}[0.1]{}% + \moodleset{fractiontol=0.1} %This sets the default. +% \end{macrocode} % \subsubsection{Keys for multiple choice questions} % \begin{macrocode} %% SINGLE and MULTIPLE -- for multichoice, is there 1 right answer or more than 1? \define@boolkey{moodle}[moodle@]{single}[true]{}% \generate@moodle@write@code*{single}<single>{\ifmoodle@single true\else false\fi}% - \moodleset{single}% + \moodleset{single=true}% %The key 'multiple' is an antonym to 'single'. \define@boolkey{moodle}[moodle@]{multiple}[true]{\ifmoodle@multiple\moodle@singlefalse\else\moodle@singletrue\fi}% @@ -1054,15 +2175,15 @@ \generate@moodle@write@code*{shuffle}<shuffleanswers>{\ifmoodle@shuffle 1\else 0\fi}% \moodleset{shuffle=true}% -%% TO DO: CORRECTFEEDBACK -%% TO DO: PARTIALLYCORRECTFEEDBACK -%% TO DO: INCORRECTFEEDBACK -%% TO DO: NUMCORRECT key +%% TODO: CORRECTFEEDBACK +%% TODO: PARTIALLYCORRECTFEEDBACK +%% TODO: INCORRECTFEEDBACK +%% TODO: NUMCORRECT key %% NUMBERING -- for numbering of multichoice questions \define@choicekey{moodle}{numbering}% {alpha,alph,Alpha,Alph,arabic,roman,Roman,% - abc,ABC,123,iii,IIII,none}[abc]{% + abc,ABCD,123,iii,IIII,none}[abc]{% \def\moodle@numbering{#1}% \def\test@@i{#1}% \ifx\test@@i\@moodle@alpha @@ -1070,9 +2191,9 @@ \ifx\test@@i\@moodle@alph \def\moodle@numbering{abc}\fi \ifx\test@@i\@moodle@Alpha - \def\moodle@numbering{ABC}\fi + \def\moodle@numbering{ABCD}\fi \ifx\test@@i\@moodle@Alph - \def\moodle@numbering{ABC}\fi + \def\moodle@numbering{ABCD}\fi \ifx\test@@i\@moodle@arabic \def\moodle@numbering{123}\fi \ifx\test@@i\@moodle@roman @@ -1094,7 +2215,7 @@ \def\@moodle@roman{roman}% \def\@moodle@Roman{Roman}% \def\@moodle@abc{abc}% - \def\@moodle@ABC{ABC}% + \def\@moodle@ABCD{ABCD}% \def\@moodle@arabicnumbers{123}% \def\@moodle@iii{iii}% \def\@moodle@IIII{IIII}% @@ -1103,7 +2224,7 @@ \ifx\moodle@numbering\@moodle@abc \renewcommand\theenumii{\alph{enumii}}% \fi - \ifx\moodle@numbering\@moodle@ABC + \ifx\moodle@numbering\@moodle@ABCD \renewcommand\theenumii{\Alph{enumii}}% \fi \ifx\moodle@numbering\@moodle@arabicnumbers @@ -1119,7 +2240,7 @@ \renewcommand\labelenumii{$\bullet$~}% \fi } - %TO DO: * In the PDF, how should 'none' in a multi look different from + %TODO: * In the PDF, how should 'none' in a multi look different from % short answer or numerical options? % * Instead of \theenumi and \labelenumi, % use \@enumdepth to automatically set the correct depth. @@ -1140,7 +2261,7 @@ %tolerance is given within the answer tag, %like <answer fraction=100 tolerance=0.03>. -% TO DO: implement unit-handling for numerical questions! +% TODO: implement unit-handling for numerical questions! % \end{macrocode} % \subsubsection{Keys for short answer questions} % \begin{macrocode} @@ -1215,7 +2336,7 @@ %% RESPONSE REQUIRED \define@boolkey{moodle}[moodle@]{response required}[true]{}% - % TO DO: Make synonym 'required' + % TODO: Make synonym 'required' \generate@moodle@write@code*{responserequired}<responserequired>{\csname ifmoodle@response required\endcsname 1\else 0\fi}% \moodleset{response required=false}% @@ -1245,7 +2366,7 @@ %% RESPONSE TEMPLATE \define@key{moodle}{template}{\long\def\moodle@responsetemplate{#1}}% - \generate@moodle@write@code{responsetemplate}<responsetemplate>{\moodle@responsetemplate} + \generate@moodle@write@html@noptag{responsetemplate}<responsetemplate>{\moodle@responsetemplate} \moodleset{template={}}% % \end{macrocode} % \subsubsection{Hint tags} @@ -1269,7 +2390,7 @@ }% \moodleset{clearwrong=false}% -% TO DO: Implement hints +% TODO: Implement hints % \end{macrocode} % % \subsection{Answer handling} @@ -1305,21 +2426,6 @@ % \subsubsection{Not yet implemented} % % \begin{macrocode} -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% TRUE/FALSE QUESTIONS %%%%%%%%%%%%%%%%% - -% TODO: implement the \writetruefalsequestion -% and a front-end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% DESCRIPTION 'QUESTIONS' %%%%%%%%%%%%% - -% TODO: implement the \writedescription and a suitable front-end. -% Should this be \begin{description}...\end{description}, -% or should \begin{quiz}...\end{quiz} just scoop up all -% text outside question environments and package it in descriptions? - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% CALCULATED %%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1354,48 +2460,142 @@ % \end{Verbatim} % % \begin{macrocode} -\def\moodle@makefrontend#1{% + +\def\moodle@makelatextagbox#1{% +% \ifmoodle@tikzloaded +% \tikzset{external/export next=false} +% \tikz[baseline]{\node[draw,minimum height=1.2em,rounded corners,fill=black!20] {\tiny #1};} +% % Fancy but interferes with the tikzexternalize counter +% \else + \Ovalbox{\tiny #1} + %\ovalbox{\tiny #1} + %\shadowbox{\tiny #1} +% \fi +}% + +\def\moodle@makelatextag@qtype#1{% + \doublebox{\tiny \textsc{#1}} +}% + +\def\moodle@makelatextag@value#1#2{% + \moodle@makelatextagbox{\csname moodle@#1\endcsname~#2} +}% + +\def\moodle@makelatextag@key#1{% + \moodle@makelatextagbox{\csname moodle@#1\endcsname} +}% + +\def\moodle@marks#1{point\ifdim#1pt=1pt \else s\fi} + +\def\moodle@makefrontend#1#2{% \NewEnviron{#1}[2][]{% \bgroup \setkeys{moodle}{##1,questionname={##2}}% \expandafter\gatheritems\xa{\BODY}% \let\moodle@questionheader=\gatheredheader %First, the LaTeX processing - \item \textbf{\moodle@questionname}\par + \item \textbf{\moodle@questionname} + \ifmoodle@handout + \moodle@makelatextag@qtype{#1} + \else + \xa\xdef\xa\test@iii\xa{\moodle@tags}% + \ifx\test@iii\@moodle@empty\relax\else + \hfill tags: \texttt{\moodle@tags} + \fi + \par + \noindent + \moodle@makelatextag@qtype{#1} + \moodle@makelatextag@value{default grade}{\moodle@marks{\csname moodle@default grade\endcsname}} + \moodle@makelatextag@value{penalty}{penalty} + \fi + #2\par \noindent \moodle@questionheader + \edef\moodle@generalfeedback{\expandonce\moodle@feedback} \csname moodle@#1@latexprocessing\endcsname %Now, writing information to XML \@moodle@ifgeneratexml{% \xa\questiontext\xa{\moodle@questionheader}% Save the question text. + \csname write#1question\endcsname \bgroup \gdef\moodle@answers@xml{}% \setkeys{moodle}{feedback={}}% \xa\loopthroughitemswithcommand\xa{\csname save#1answer\endcsname}% \passvalueaftergroup{\moodle@answers@xml}% \egroup - \csname write#1question\endcsname + \moodle@writeanswers% + \moodle@writetags% + \writetomoodle{</question>}% }{}% \egroup }% } % \end{macrocode} % -% \subsubsection{Essay Question Front-End} -% The essay question is the only question type whose front end +% \subsubsection{Description Question Front-End} +% Description and essay questions are the only question types whose front end % is not yet created by |\moodle@makefrontend|. -% This is because of what it must do with its |\item|'s. +% This is because of what need to be done with their contents. +% +% Description blocks can be empty. In this case, nothing is done. +% +% \begin{macrocode} +\let\description\relax% remove the meaning of existing \description and \enddescription +\let\enddescription\relax +\NewEnviron{description}[2][]{% + \bgroup + \setkeys{moodle}{#1,questionname={#2}}% + \let\moodle@questiontext=\BODY + \trim@spaces@in\moodle@questiontext + \ifx\moodle@questiontext\@empty\relax\else% + %First, the LaTeX processing. + \item \textbf{\moodle@questionname} + \ifmoodle@handout\else + \xa\xdef\xa\test@iii\xa{\moodle@tags}% + \ifx\test@iii\@moodle@empty\relax\else + \hfill tags: \texttt{\moodle@tags}% + \fi + \par + \noindent + \fi + \moodle@makelatextag@qtype{description}\par + \noindent + \moodle@questiontext\par + \ifmoodle@handout\else + \ifx\moodle@feedback\@empty\relax\else + \fbox{\parbox{\linewidth}{\emph{\moodle@feedback}}}% + \fi + \fi + %Now, writing information to memory. + \@moodle@ifgeneratexml{% + \writetomoodle{<question type="description">}% + \moodle@writecommondata + \moodle@writetags% + \writetomoodle{</question>}% + }{}% + \fi + \egroup +}% +% \end{macrocode} +% +% \subsubsection{Essay Question Front-End} +% The front end is not yet created by |\moodle@makefrontend| because of +% what must must be done with the |\item|'s. % % \begin{macrocode} \def\moodle@essay@latexprocessing{% % Moodle cannot automatically grade an essay, % but if the user puts \item's in, we can list them in an itemize as notes. + \par\noindent \emph{Notes for grader:} + \moodle@questionheader \ifnum\c@numgathereditems>0\relax - \par\noindent Notes: (not included in XML) \begin{itemize} \setlength\itemsep{0pt}\setlength\parskip{0pt}% \loopthroughitemswithcommand{\moodle@print@essay@answer}% \end{itemize}% \fi + \ifx\moodle@generalfeedback\@empty\relax\else% + \fbox{\parbox{\linewidth}{\emph{\moodle@generalfeedback}}}% + \fi } \NewEnviron{essay}[2][]{% @@ -1404,13 +2604,35 @@ \expandafter\gatheritems\expandafter{\BODY}% \let\moodle@questionheader=\gatheredheader %First, the LaTeX processing. - \item \textbf{\moodle@questionname}\par + \item \textbf{\moodle@questionname} + \ifmoodle@handout + \moodle@makelatextag@qtype{essay} + \else + \xa\xdef\xa\test@iii\xa{\moodle@tags}% + \ifx\test@iii\@moodle@empty\relax\else + \hfill tags: \texttt{\moodle@tags} + \fi + \par + \noindent + \moodle@makelatextag@qtype{essay} + \moodle@makelatextag@value{default grade}{\moodle@marks{\csname moodle@default grade\endcsname}} + \moodle@makelatextag@value{penalty}{penalty} + \moodle@makelatextag@key{responseformat} + \fi + \par \noindent - \moodle@questionheader - \csname moodle@essay@latexprocessing\endcsname + \ifx\moodle@responsetemplate\@empty\relax\else% + \par\noindent \emph{Template:} + \par\fbox{\parbox{\linewidth}{\moodle@responsetemplate}}\par + \fi + \edef\moodle@generalfeedback{\expandonce\moodle@feedback} + \ifmoodle@handout\else + \csname moodle@essay@latexprocessing\endcsname + \fi %Now, writing information to memory. \@moodle@ifgeneratexml{% \xa\questiontext\xa{\moodle@questionheader}% Save the question text. + \writeessayquestion \bgroup \gdef\moodle@answers@xml{}% % @@ -1430,12 +2652,15 @@ % \passvalueaftergroup{\moodle@answers@xml}% \egroup - \writeessayquestion + \moodle@writeanswers% The 'answers' XML really contains the grader info. + \moodle@writeresponsetemplate% + \moodle@writetags% + \writetomoodle{</question>}% }{}% \egroup }% -%%%% TO DO +%%%% TODO %%%% To make essay work will be tough. %%%% Every line from \ifnum\c@numgathereditems=0\relax through its \else and \fi, %%%% with the exception of @@ -1477,9 +2702,6 @@ \moodle@writeresponsefieldlines% \moodle@writeattachmentsallowed% \moodle@writeattachmentsrequired% - \moodle@writeanswers% The 'answers' XML really contains the grader info. - \moodle@writeresponsetemplate% - \writetomoodle{</question>}% }% \def\moodle@print@essay@answer#1{% @@ -1488,6 +2710,7 @@ \def\moodle@savegraderinfo#1{% + %\def\ds{#1}\show\ds \bgroup \moodle@savegraderinfo@int#1\moodle@answer@rdelim \passvalueaftergroup{\moodle@answers@xml}% @@ -1498,7 +2721,8 @@ \moodle@savegraderinfo@int@int% }% \def\moodle@savegraderinfo@int@int#1\moodle@answer@rdelim{% - \xa\converttohtmlmacro\xa\moodle@answertext@html\xa{#1}% + \def\moodle@answertext{#1} + \xa\converttohtmlmacro\xa\moodle@answertext@html\xa{\moodle@answertext}% %\trim@spaces@in\moodle@answertext \ifnum\c@numgathereditems>1\relax \addto@xml[6]{\moodle@answers@xml}{<li>\moodle@answertext@html</li>}% @@ -1512,17 +2736,21 @@ % % \begin{macrocode} \def\blank{\rule{1in}{0.5pt}}% -% TO DO: Make an optional argument for width? This wouldn't affect Moodle, +% TODO: Make an optional argument for width? This wouldn't affect Moodle, % only the appearance in the PDF. It doesn't seem worth it. -% \NewEnviron{shortanswer}[2][]{% +%\NewEnviron{shortanswer}[2][]{% % \bgroup % \setkeys{moodle}{#1,questionname={#2}}% % \expandafter\gatheritems\xa{\BODY}% % \let\moodle@questionheader=\gatheredheader % %First, the LaTeX processing. -% \bigskip\par\noindent -% \fbox{\textbf{\moodle@questionname}}\par +% \item \textbf{\moodle@questionname} +% \csname ifmoodle@case sensitive\endcsname +% \framebox{\tiny Case-Sensitive}\relax +% \fi +% \framebox{\tiny\csname moodle@default grade\endcsname~points} +% \framebox{\tiny\csname moodle@penalty\endcsname~penalty}\par % \noindent % \moodle@questionheader % \csname moodle@shortanswer@latexprocessing\endcsname @@ -1532,7 +2760,8 @@ % \bgroup % \gdef\moodle@answers@xml{}% % \setkeys{moodle}{feedback={}}% -% \xa\loopthroughitemswithcommand\xa{\csname saveshortansweranswer\endcsname}% +% \xa\loopthroughitemswithcommand\xa{\csname +% saveshortansweranswer\endcsname}% % \passvalueaftergroup{\moodle@answers@xml}% % \egroup % \csname writeshortanswerquestion\endcsname @@ -1540,16 +2769,33 @@ % \egroup % }% -\moodle@makefrontend{shortanswer}% +\moodle@makefrontend{shortanswer}{\moodle@makelatextag@shortanswer}% % LATEX PROCESSING -\def\moodle@shortanswer@latexprocessing{% - \begin{itemize} \setlength\itemsep{0pt}\setlength\parskip{0pt}% - \loopthroughitemswithcommand{\moodle@print@shortanswer@answer}% - \end{itemize}% + +\def\moodle@makelatextag@shortanswer{% + \csname ifmoodle@case sensitive\endcsname + \moodle@makelatextagbox{Case-Sensitive}\relax + \else + \moodle@makelatextagbox{Case-Insensitive}\relax + \fi } +\ifmoodle@handout + \let\moodle@shortanswer@latexprocessing\relax +\else + \def\moodle@shortanswer@latexprocessing{% + \begin{itemize} \setlength\itemsep{0pt}\setlength\parskip{0pt}% + \loopthroughitemswithcommand{\moodle@print@shortanswer@answer}% + \end{itemize}% + \ifx\moodle@generalfeedback\@empty\relax\else% + \fbox{\parbox{\linewidth}{\emph{\moodle@generalfeedback}}}% + \fi + } +\fi + \def\moodle@print@shortanswer@answer#1{% + \let\moodle@feedback=\@empty \moodle@print@shortanswer@answer@int#1\@rdelim }% \newcommand\moodle@print@shortanswer@answer@int[1][]{% @@ -1558,9 +2804,13 @@ }% \def\moodle@print@shortanswer@answer@int@int#1\@rdelim{% \ifx\moodle@fraction\@hundred - \item #1\quad$\checkmark$% + \item #1$~\checkmark$% \else - \item #1\quad (\moodle@fraction\%)% + \moodle@checkfraction + \item #1$~(\moodle@fraction\%)$% + \fi + \ifx\moodle@feedback\@empty\relax\else + \hfill \emph{$\rightarrow$ \moodle@feedback} \fi }% @@ -1577,10 +2827,14 @@ }% \def\saveshortansweranswer@int@int#1\moodle@answer@rdelim{% \def\moodle@answertext{#1}% + \trim@spaces@in\moodle@answertext + \moodle@checkfraction \addto@xml[2]{\moodle@answers@xml}{<answer fraction="\moodle@fraction" format="plain_text">}% \addto@xml[4]{\moodle@answers@xml}{ <text>\moodle@answertext</text>}% \ifx\moodle@feedback\@empty\relax\else - \addto@xml[4]{\moodle@answers@xml}{ <feedback format="html"><text><![CDATA[<p>\moodle@feedback</p>]]></text></feedback>}% + \trim@spaces@in\moodle@feedback + \xa\converttohtmlmacro\xa\moodle@feedback@html\xa{\moodle@feedback}% + \addto@xml[4]{\moodle@answers@xml}{ <feedback format="html"><text><![CDATA[<p>\moodle@feedback@html</p>]]></text></feedback>}% \fi \addto@xml[2]{\moodle@answers@xml}{</answer>}% }% @@ -1590,40 +2844,63 @@ \writetomoodle{<question type="shortanswer">}% \moodle@writecommondata% \moodle@writeusecase% - \moodle@writeanswers% - \writetomoodle{</question>}% }% % \end{macrocode} % % \subsubsection{Numerical Question Front-End} % % \begin{macrocode} -\moodle@makefrontend{numerical}% +\moodle@makefrontend{numerical}{\moodle@makelatextag@numerical}% % LATEX PROCESSING -\def\moodle@numerical@latexprocessing{% - \begin{itemize} \setlength\itemsep{0pt}\setlength\parskip{0pt}% - \loopthroughitemswithcommand{\moodle@print@numerical@answer}% - \end{itemize}% + +\def\moodle@makelatextag@numerical{} + +\AtEndPreamble{ + \@ifpackageloaded{siunitx}{\def\moodle@printnum{\num[omit-uncertainty,copy-decimal-marker]}}{\let\moodle@printnum\trim@spaces}% } +\ifmoodle@handout + \let\moodle@numerical@latexprocessing\relax +\else + \def\moodle@numerical@latexprocessing{% + \begin{itemize} \setlength\itemsep{0pt}\setlength\parskip{0pt}% + \loopthroughitemswithcommand{\moodle@print@numerical@answer}% + \end{itemize}% + \ifx\moodle@generalfeedback\@empty\relax\else% + \fbox{\parbox{\linewidth}{\emph{\moodle@generalfeedback}}}% + \fi + } +\fi + \def\moodle@print@numerical@answer#1{% - \moodle@print@numerical@answer@int#1\@rdelim + \let\moodle@feedback=\@empty + \bgroup + \moodle@print@numerical@answer@int#1\@rdelim + \egroup }% \newcommand\moodle@print@numerical@answer@int[1][]{% \setkeys{moodle}{#1}% \moodle@print@numerical@answer@int@int% }% \def\moodle@print@numerical@answer@int@int#1\@rdelim{% - \ifdim0pt=\moodle@tolerance pt\relax - \def\moodle@numericalprint@tolerance{}% + \xdef\test@i{\trim@spaces{#1}}% + \ifx\test@i\@star + \item \test@i \else - \edef\moodle@numericalprint@tolerance{\noexpand\pm\moodle@tolerance}% + \item \moodle@printnum{#1}% + \ifnum\z@=\moodle@tolerance\else + $\,\pm\,$\moodle@printnum{\moodle@tolerance}% + \fi \fi \ifx\moodle@fraction\@hundred - \item $#1\moodle@numericalprint@tolerance\quad\checkmark$% + $~\checkmark$% \else - \item $#1\moodle@numericalprint@tolerance$\quad (\moodle@fraction\%)% + \moodle@checkfraction + $~(\moodle@fraction\%)$% + \fi + \ifx\moodle@feedback\@empty\relax\else + \hfill \emph{$\rightarrow$ \moodle@feedback}% \fi }% @@ -1640,11 +2917,17 @@ }% \def\savenumericalanswer@int@int#1\moodle@answer@rdelim{% \def\moodle@answertext{#1}% + \trim@spaces@in\moodle@answertext + \moodle@checkfraction \addto@xml[2]{\moodle@answers@xml}{<answer fraction="\moodle@fraction" format="plain_text">}% \addto@xml[4]{\moodle@answers@xml}{ <text>\moodle@answertext</text>}% - \addto@xml[4]{\moodle@answers@xml}{ <tolerance>\moodle@tolerance</tolerance>}% + \ifx\moodle@answertext\@star\else + \addto@xml[4]{\moodle@answers@xml}{ <tolerance>\moodle@tolerance</tolerance>}% + \fi \ifx\moodle@feedback\@empty\relax\else - \addto@xml[4]{\moodle@answers@xml}{ <feedback format="html"><text><![CDATA[<p>\moodle@feedback</p>]]></text></feedback>}% + \trim@spaces@in\moodle@feedback + \xa\converttohtmlmacro\xa\moodle@feedback@html\xa{\moodle@feedback}% + \addto@xml[4]{\moodle@answers@xml}{ <feedback format="html"><text><![CDATA[<p>\moodle@feedback@html</p>]]></text></feedback>}% \fi \addto@xml[2]{\moodle@answers@xml}{</answer>}% }% @@ -1654,8 +2937,6 @@ \gdef\writenumericalquestion{% \writetomoodle{<question type="numerical">}% \moodle@writecommondata% - \moodle@writeanswers% - \writetomoodle{</question>}% }% % \end{macrocode} % @@ -1670,57 +2951,155 @@ % \item 11 % \end{multi}% -\moodle@makefrontend{multi} +\moodle@makefrontend{multi}{\moodle@makelatextag@multi}% % LATEX PROCESSING + +\def\moodle@makelatextag@multi{% + \ifmoodle@multiple + \moodle@makelatextagbox{Multiple}\relax% + \else + \moodle@makelatextagbox{Single}\relax% + \fi + \ifmoodle@handout\else + \ifmoodle@shuffle + \moodle@makelatextagbox{Shuffle}\relax% + \fi + \fi +} + \def\moodle@multi@latexprocessing{% - \moodle@countcorrectanswers - \begin{enumerate}\moodle@obeynumberingstyle - %\renewcommand{\theenumi}{\alph{enumi}}% - \setlength\itemsep{0pt}\setlength\parskip{0pt}% - \loopthroughitemswithcommand{\moodle@print@multichoice@answer}% - \end{enumerate}% + \moodle@countcorrectanswers% + \ifmoodle@handout\NewList{answerlist}\fi + \begin{enumerate}\moodle@obeynumberingstyle% + %\renewcommand{\theenumi}{\alph{enumi}}% + \setlength\itemsep{0pt}\setlength\parskip{0pt}% + \loopthroughitemswithcommand{\moodle@print@multichoice@answer}% + \ifmoodle@handout + \ifmoodle@shuffle + \let\moodle@multi@loop=\ForEachRandomItem + \else + \let\moodle@multi@loop=\ForEachFirstItem + \fi + \moodle@multi@loop{answerlist}{Answer}{\Answer}% + \fi + \end{enumerate}% + \ifmoodle@handout\else + \ifx\moodle@generalfeedback\@empty\relax\else% + \fbox{\parbox{\linewidth}{\emph{\moodle@generalfeedback}}}% + \fi + \fi } \def\moodle@print@multichoice@answer#1{% - \moodle@print@multichoice@answer@int#1\@rdelim + \let\moodle@feedback=\@empty% + \moodle@print@multichoice@answer@int#1 \@rdelim% }% \newcommand\moodle@print@multichoice@answer@int[1][]{% - \setkeys{moodle}{fraction=0,#1}% - \moodle@print@multichoice@answer@int@int + \let\moodle@fraction\@empty% + \setkeys{moodle}{#1}% + \moodle@print@multichoice@answer@int@int% }% \def\moodle@print@multichoice@answer@int@int#1#2\@rdelim{% \def\test@i{#1}% - \ifmoodle@single - \ifx\test@i\@star - \item #2$~\checkmark$% + \def\test@ii{#2}% + \def\moodle@answertext{\item }% + \ifx\test@i\@star% + \g@addto@macro\moodle@answertext{#2}% + \ifmoodle@single% + \setkeys{moodle}{fraction=100}% \else - \item #1#2% + \setkeys{moodle}{fraction=\moodle@autopoints}% \fi \else - \ifx\test@i\@star - \item #2% - \setkeys{moodle}{fraction=\moodle@autopoints}% - \else - \item #1#2% + \g@addto@macro\moodle@answertext{#1#2}% + \fi + \trim@spaces@in\moodle@answertext% + \trim@spaces@in\moodle@answertext% + \ifmoodle@handout\else + \ifmoodle@single% + \ifx\moodle@fraction\@empty\relax% + \setkeys{moodle}{fraction=0}% + \fi + \ifx\moodle@fraction\@hundred% + \trim@spaces@in\moodle@answertext% + \g@addto@macro\moodle@answertext{$~\checkmark$}% + \else + \moodle@checkfraction + \ifdim0pt=\moodle@fraction pt\relax\else% + \g@addto@macro\moodle@answertext{$~(\moodle@fraction\%)$}% + \fi + \fi + \else% multiple + \ifx\moodle@fraction\@empty\relax% + \setkeys{moodle}{fraction=\moodle@autosanctions}% + \fi + \moodle@checkfraction + \g@addto@macro\moodle@answertext{$~(\moodle@fraction\%)$}% \fi - $~(\moodle@fraction\%)$ + \fi + \ifmoodle@handout + \def\temp{\InsertLastItem{answerlist}}% + \xa\temp\xa{\moodle@answertext}% + \else + \ifx\moodle@feedback\@empty\relax\else% + \g@addto@macro\moodle@answertext{\hfill \emph{$\rightarrow$ \moodle@feedback}}% + \fi + \moodle@answertext \fi }% -% COMMON UTILITY: COUNTING CORRECT ANSWERS - \newcounter{moodle@numcorrectanswers}% +% COMMON UTILITY: COUNTING CORRECT ANSWERS (AND A BIT MORE...) + \newcounter{moodle@numcorrectanswers}% count the stars + \newcounter{moodle@numincorrectanswers}% count the items without fraction key indicated \newlength{\moodle@pointspercorrect}% + \newlength{\moodle@pointsperincorrect}% + \newlength{\moodle@sumofpositivefractions}% sums user-set positive fractions + \newlength{\moodle@sumofnegativefractions}% sums user-set negative fractions \def\moodle@countcorrectanswers{% \setcounter{moodle@numcorrectanswers}{0}% + \setcounter{moodle@numincorrectanswers}{0}% \global\setlength{\moodle@pointspercorrect}{100pt}% + \global\setlength{\moodle@pointsperincorrect}{-100pt}% + \global\setlength{\moodle@sumofpositivefractions}{0pt}% + \global\setlength{\moodle@sumofnegativefractions}{0pt}% \loopthroughitemswithcommand{\moodle@countcorrectanswers@a}% - \ifnum0=\c@moodle@numcorrectanswers\relax - \PackageError{moodle}{No correct answers given for multiple choice question.}{Please mark at least one answer correct.}% - \gdef\moodle@autopoints{0}% + \global\advance\moodle@pointspercorrect by-\moodle@sumofpositivefractions\relax% + \def\ds{\strip@pt\moodle@sumofpositivefractions}% + \ifnum0=\c@moodle@numcorrectanswers\relax% + % autopoints will never be used but we check if the sum of positive fractions is 100% + \ifdim\moodle@pointspercorrect<-\moodle@fractiontol pt\relax% + \PackageWarning{moodle}{Positive fractions sum up to more than 100 (here: \ds)}% + \else + \ifdim\moodle@pointspercorrect>\moodle@fractiontol pt\relax% + \PackageError{moodle}{Positive fractions sum up to less than 100 (here: \ds)}% + \fi + \fi \else - \global\divide\moodle@pointspercorrect by \c@moodle@numcorrectanswers\relax - \edef\moodle@autopoints{\strip@pt\moodle@pointspercorrect}% + \ifdim0pt<\moodle@pointspercorrect\relax\else% + % we have starred items so the sum of user-set positive fractions must be less than 100% + % otherwise, starred items would lead to penalties + \PackageError{moodle}{Positive fractions sum up to 100 or more (here: \ds): + there is no positive points left to be given to starred items.}% + \fi + \global\divide\moodle@pointspercorrect by \c@moodle@numcorrectanswers\relax% \fi + \gdef\moodle@autopoints{\strip@pt\moodle@pointspercorrect}% + \global\advance\moodle@pointsperincorrect by-\moodle@sumofnegativefractions\relax% + \def\ds{\strip@pt\moodle@sumofnegativefractions}% + \ifnum0=\c@moodle@numincorrectanswers\relax% + % autosanctions will never be used and + % we do not care about the sum of negative fractions (might be less than -100) + \else + \ifdim0pt<\moodle@pointsperincorrect\relax% + % we have items without fractions set: to prevent auto sanctions from becoming bonuses, + % such items are neutralized. + \PackageWarning{moodle}{Negative fractions sum up to -100 or less (here: \ds): + items with no fraction key set will be considered as neutral.}% + \global\setlength{\moodle@pointsperincorrect}{0pt}% + \fi + \global\divide\moodle@pointsperincorrect by \c@moodle@numincorrectanswers\relax% + \fi + \gdef\moodle@autosanctions{\strip@pt\moodle@pointsperincorrect}% } \def\moodle@countcorrectanswers@a#1{% %The grouping is to keep key answer-specific key changes local. @@ -1729,34 +3108,79 @@ \egroup }% \newcommand\moodle@countcorrectanswers@b[1][]{% - \setkeys{moodle}{fraction=0,#1}% - \moodle@countcorrectanswers@c% + %\ifx&% + \let\moodle@fraction\@empty% + \setkeys{moodle}{#1}% + \moodle@countcorrectanswers@c% + %\fi }% \def\moodle@countcorrectanswers@c#1#2\moodle@answer@rdelim{% \def\test@i{#1}% \ifx\test@i\@star \stepcounter{moodle@numcorrectanswers}% \else - \global\addtolength{\moodle@pointspercorrect}{-\moodle@fraction pt}% + \ifx\moodle@fraction\@empty\relax% + \stepcounter{moodle@numincorrectanswers}% + \else + \ifdim0pt<\moodle@fraction pt\relax% + \global\addtolength{\moodle@sumofpositivefractions}{\moodle@fraction pt}% + \else + \global\addtolength{\moodle@sumofnegativefractions}{\moodle@fraction pt}% + \fi + \fi \fi }% - \def\moodle@setautopoints#1pt{% - \gdef\moodle@autopoints{#1}% + \newlength{\test@fraction}% + \newlength{\test@lower}% + \newlength{\test@upper}% + \def\moodle@fractionerror{% + \def\ds{\moodle@fraction}% + \PackageError{moodle}{the current fraction is not a valid value (here: \ds)}% + } + {\catcode`|=3\relax + \gdef\moodle@validfractionlist{0|5|10|11.11111|12.5|14.28571|16.66667|20|25|30|33.33333|40|50|60|66.66667|70|75|80|83.33333|90|100}}% + \def\moodle@isfractionnear#1{% + \setlength{\test@lower}{#1 pt}% + \addtolength{\test@lower}{-\moodle@fractiontol pt}% + \setlength{\test@upper}{#1 pt}% + \addtolength{\test@upper}{\moodle@fractiontol pt}% + \ifdim\test@upper>\test@fraction\relax + \ifdim\test@lower<\test@fraction\relax + \gdef\test@fractionmatched{#1}% + \fi + \fi + } + \def\moodle@checkfraction{% + %\def\test@i{#1}% + \setlength{\test@fraction}{\moodle@fraction pt}% + % take the absolute value + \ifdim0pt>\test@fraction\relax% + \setlength{\test@fraction}{-\moodle@fraction pt}% + \fi + % test if the fraction is an admissible value + \let\test@fractionmatched\@empty + \forlistloop{\moodle@isfractionnear}{\moodle@validfractionlist}% + \ifx\test@fractionmatched\@empty\relax + \moodle@fractionerror% + \fi + \ifdim\moodle@fraction pt<-\moodle@fractiontol pt\relax% + \setkeys{moodle}{fraction=-\test@fractionmatched}% + \else + \setkeys{moodle}{fraction=\test@fractionmatched}% + \fi } - -% TODO: What if rounding errors make the percentages not add to 100%? Will Moodle complain? -% TODO: Verify that negative scores are handled correctly. % TODO: Put these macros in same order as other sections'. % SAVING ANSWERS TO MEMORY \def\savemultianswer#1{% \bgroup - \savemultianswer@int#1\moodle@answer@rdelim + \savemultianswer@int#1 \moodle@answer@rdelim \passvalueaftergroup{\moodle@answers@xml}% \egroup }% \newcommand\savemultianswer@int[1][]{% - \setkeys{moodle}{fraction=0,#1}% + \let\moodle@fraction\@empty% + \setkeys{moodle}{#1}% \savemultianswer@int@int% }% \def\savemultianswer@int@int#1#2\moodle@answer@rdelim{% @@ -1771,12 +3195,23 @@ \else \def\moodle@answertext{#1#2}% \fi + \ifx\moodle@fraction\@empty\relax% + \ifmoodle@single\relax + \setkeys{moodle}{fraction=0}% + \else% multiple + \setkeys{moodle}{fraction=\moodle@autosanctions}% + \fi + \fi + \trim@spaces@in\moodle@answertext \trim@spaces@in\moodle@answertext + \moodle@checkfraction \addto@xml[2]{\moodle@answers@xml}{<answer fraction="\moodle@fraction" format="html">}% \xa\converttohtmlmacro\xa\moodle@answertext@html\xa{\moodle@answertext}% \addto@xml[4]{\moodle@answers@xml}{ <text><![CDATA[<p>\moodle@answertext@html</p>]]></text>}% \ifx\moodle@feedback\@empty\relax\else - \addto@xml[4]{\moodle@answers@xml}{ <feedback format="html"><text><![CDATA[<p>\moodle@feedback</p>]]></text></feedback>}% + \trim@spaces@in\moodle@feedback + \xa\converttohtmlmacro\xa\moodle@feedback@html\xa{\moodle@feedback}% + \addto@xml[4]{\moodle@answers@xml}{ <feedback format="html"><text><![CDATA[<p>\moodle@feedback@html</p>]]></text></feedback>}% \fi \addto@xml[2]{\moodle@answers@xml}{</answer>}% }% @@ -1788,50 +3223,351 @@ \moodle@writesingle% \moodle@writeshuffle% \moodle@writeanswernumbering% - \moodle@writeanswers% - \writetomoodle{</question>}% }% % \end{macrocode} % -% \subsubsection{Matching Question Front-End} +% \subsubsection{True/False Question Front-End} % % \begin{macrocode} -\let\answer=\hfill +% True/False has structure +% \begin{truefalse}[options]{name}% +% This is a matching question. +% \item[feedback={feedback for student answering incorrectly "true"}] % first item is for true +% \item* this is an other way of specifying answer-specific feedback +% \end{truefalse}% + +%\moodle@makefrontend{truefalse}{}% We dont use the generic frontend because truefalse has no tunable penalty -\moodle@makefrontend{matching} +\NewEnviron{truefalse}[2][]{% + \bgroup + \setkeys{moodle}{#1,questionname={#2}}% + \expandafter\gatheritems\xa{\BODY}% + \let\moodle@questionheader=\gatheredheader + %First, the LaTeX processing + \item \textbf{\moodle@questionname} + \ifmoodle@handout + \moodle@makelatextag@qtype{truefalse} + \else + \xa\xdef\xa\test@iii\xa{\moodle@tags}% + \ifx\test@iii\@moodle@empty\relax\else + \hfill tags: \texttt{\moodle@tags} + \fi + \par + \noindent + \moodle@makelatextag@qtype{truefalse} + \moodle@makelatextag@value{default grade}{\moodle@marks{\csname moodle@default grade\endcsname}} + \fi + \par + \noindent + \moodle@questionheader + \edef\moodle@generalfeedback{\expandonce\moodle@feedback} + \moodle@truefalse@latexprocessing + %Now, writing information to XML + \@moodle@ifgeneratexml{% + \setkeys{moodle}{penalty=1}% + \xa\questiontext\xa{\moodle@questionheader}% Save the question text. + \csname writetruefalsequestion\endcsname + \bgroup + \gdef\moodle@answers@xml{}% + \setkeys{moodle}{feedback={}}% + \xa\loopthroughitemswithcommand\xa{\xa\savetruefalseanswer}% + \ifnum\c@numgathereditems=1\relax% + \setcounter{currentitemnumber}{2}% + \savetruefalseanswer{} + \fi + \passvalueaftergroup{\moodle@answers@xml}% + \egroup + \moodle@writeanswers% + \moodle@writetags% + \writetomoodle{</question>}% + }{}% + \egroup + }% % LATEX PROCESSING -\def\moodle@matching@latexprocessing{% + +\def\moodle@truefalse@latexprocessing{% +% \ifnum\c@numgathereditems>2\relax% +% \PackageError{moodle}{Expecting at max two answers with truefalse type} +% \fi + \setcounter{moodle@numcorrectanswers}{0}% + \begin{itemize} \setlength\itemsep{0pt}\setlength\parskip{0pt}% + \loopthroughitemswithcommand{\moodle@print@truefalse@answer}% + \ifnum\c@currentitemnumber=2% + \item \textbf{False}% + \fi + \end{itemize} + \ifmoodle@handout\else + \ifx\moodle@generalfeedback\@empty\relax\else% + \fbox{\parbox{\linewidth}{\emph{\moodle@generalfeedback}}}% + \fi + \fi + \ifnum\c@moodle@numcorrectanswers=0\relax% + \PackageError{moodle}{No answer is explicitly marked as correct (*). Be sure one answer leads to points.}% + \fi + \ifnum\c@moodle@numcorrectanswers>1\relax% + \PackageError{moodle}{Two answers are explicitly marked as correct (*). Be sure only one answer leads to points.}% + \fi +} + + \def\moodle@print@truefalse@answer#1{% here # is all what comes after "\item", that is "[options]* text" + \let\moodle@feedback=\@empty + \moodle@print@truefalse@answer@int#1\@rdelim % add an end delimiter: + }% + \newcommand\moodle@print@truefalse@answer@int[1][]{% with the optional argument, catch options and set them as keys + \setkeys{moodle}{#1}% + \moodle@print@truefalse@answer@int@int% applies to the rest: "* text\@rdelim" + }% + \def\moodle@print@truefalse@answer@int@int#1\@rdelim{% this is just to treat appart the case where nothing follows + \def\test@i{#1} + \trim@spaces@in\test@i + \ifx\test@i\@empty\relax + \moodle@print@truefalse@answer@int@int@empty + \else + \moodle@print@truefalse@answer@int@int@int#1\@rdelim + \fi + }% + \def\moodle@print@truefalse@answer@int@int@empty{% + \ifnum\c@currentitemnumber=1% + \def\moodle@answertext{True}% + \fi + \ifnum\c@currentitemnumber=2% + \def\moodle@answertext{False}% + \fi + \item \textbf{\moodle@answertext}% + \ifmoodle@handout\else + \ifx\moodle@feedback\@empty\relax\else + ~\hfill \emph{$\rightarrow$ \moodle@feedback}% + \fi + \fi + }% + \def\moodle@print@truefalse@answer@int@int@int#1#2\@rdelim{% + \ifnum\c@currentitemnumber=1% + \def\moodle@answertext{True}% + \fi + \ifnum\c@currentitemnumber=2% + \def\moodle@answertext{False}% + \fi + \item \textbf{\moodle@answertext}% + \ifnum\c@currentitemnumber<3% + \def\test@i{#1}% + %\trim@spaces@in\test@i + \ifx\test@i\@star + \ifmoodle@handout\else + ~$\checkmark$% + \fi + \stepcounter{moodle@numcorrectanswers}% + \else + ~% + \fi + \ifmoodle@handout\else + \ifx\moodle@feedback\@empty\relax + \def\test@ii{#2}% + \trim@spaces@in\test@ii + \ifx\test@ii\@empty\relax\else + \ifx\test@i\@star% + \hfill \emph{$\rightarrow$ #2}% + \else% + \hfill \emph{$\rightarrow$ #1#2}% + \fi + \fi + \else + \hfill \emph{$\rightarrow$ \moodle@feedback}% + \fi + \fi + \fi + }% + +% SAVING ANSWERS TO MEMORY +\def\savetruefalseanswer#1{% \bgroup - \let\answer=\hfill - \begin{enumerate}\renewcommand{\theenumi}{\alph{enumi}}\setlength\itemsep{0pt}\setlength\parskip{0pt}% - \loopthroughitemswithcommand{\moodle@print@matching@answer}% - \end{enumerate}% + \savetruefalseanswer@int#1\moodle@answer@rdelim + \passvalueaftergroup{\moodle@answers@xml}% \egroup -} - \long\def\moodle@print@matching@answer#1{% - \moodle@print@matching@answer@int#1\@rdelim +}% + \newcommand\savetruefalseanswer@int[1][]{% + \setkeys{moodle}{#1}% + \savetruefalseanswer@int@int% }% - \newcommand\moodle@print@matching@answer@int[1][]{% - \moodle@print@matching@answer@int@int\relax + \def\savetruefalseanswer@int@int#1\moodle@answer@rdelim{% + \def\test@i{#1} + \trim@spaces@in\test@i + \ifx\test@i\@empty\relax + \savetruefalseanswer@int@int@empty + \else + \savetruefalseanswer@int@int@int#1\moodle@answer@rdelim + \fi + }% + \def\savetruefalseanswer@int@int@empty{% + \setkeys{moodle}{fraction=0}% + \ifnum\c@currentitemnumber=1% + \def\moodle@answertext{true}% + \fi + \ifnum\c@currentitemnumber=2% + \def\moodle@answertext{false}% + \fi + \ifnum\c@currentitemnumber<3% + \addto@xml[2]{\moodle@answers@xml}{<answer fraction="\moodle@fraction" format="plain_text">}% + \addto@xml[4]{\moodle@answers@xml}{ <text>\moodle@answertext</text>}% + \ifx\moodle@feedback\@empty\relax\else + \trim@spaces@in\moodle@feedback + \xa\converttohtmlmacro\xa\moodle@feedback@html\xa{\moodle@feedback}% + \addto@xml[4]{\moodle@answers@xml}{ <feedback format="html"><text><![CDATA[<p>\moodle@feedback@html</p>]]></text></feedback>}% + \fi + \addto@xml[2]{\moodle@answers@xml}{</answer>}% + \fi }% - \long\def\moodle@print@matching@answer@int@int#1\answer#2\@rdelim{% - \item #1\hfill #2% + \def\savetruefalseanswer@int@int@int#1#2\moodle@answer@rdelim{% + \def\test@i{#1}% + \ifx\test@i\@star + \setkeys{moodle}{fraction=100}% + \else + \setkeys{moodle}{fraction=0}% + \fi + \ifnum\c@currentitemnumber=1% + \def\moodle@answertext{true}% + \fi + \ifnum\c@currentitemnumber=2% + \def\moodle@answertext{false}% + \fi + \ifnum\c@currentitemnumber<3% + \addto@xml[2]{\moodle@answers@xml}{<answer fraction="\moodle@fraction" format="plain_text">}% + \addto@xml[4]{\moodle@answers@xml}{ <text>\moodle@answertext</text>}% + \ifx\moodle@feedback\@empty\relax + \def\test@ii{#2} + \ifx\test@ii\@empty\relax\else + \ifx\test@i\@star + \xa\converttohtmlmacro\xa\moodle@feedback@html\xa{#2}% + \else% + \xa\converttohtmlmacro\xa\moodle@feedback@html\xa{#1#2}% + \fi% + \addto@xml[4]{\moodle@answers@xml}{ <feedback format="html"><text><![CDATA[<p>\moodle@feedback@html</p>]]></text></feedback>}% + \fi + \else + \trim@spaces@in\moodle@feedback + \xa\converttohtmlmacro\xa\moodle@feedback@html\xa{\moodle@feedback}% + \addto@xml[4]{\moodle@answers@xml}{ <feedback format="html"><text><![CDATA[<p>\moodle@feedback@html</p>]]></text></feedback>}% + \fi + \addto@xml[2]{\moodle@answers@xml}{</answer>}% + \fi }% +% WRITING QUESTION TO XML FILE +\gdef\writetruefalsequestion{% + \writetomoodle{<question type="truefalse">}% + \moodle@writecommondata% +}% +% \end{macrocode} +% +% \subsubsection{Matching Question Front-End} +% +% \begin{macrocode} +%\let\answer=\hfill + +\moodle@makefrontend{matching}{\moodle@makelatextag@matching}% + +% LATEX PROCESSING + +\def\moodle@makelatextag@matching{% + \ifmoodle@handout\else + \ifmoodle@draganddrop + \moodle@makelatextagbox{Drag and drop}\relax% + \fi + \ifmoodle@shuffle + \moodle@makelatextagbox{Shuffle}\relax% + \fi + \fi +} + +\def\moodle@matching@latexprocessing{% + \bgroup + %\let\answer=\hfill + \par%\noindent + \ifmoodle@handout + \NewList{questionlist} + \NewList{answerlist} + \fi + \def\matching@table@text{}% + % \ifnum\c@numgathereditems>2\relax% +% \PackageError{moodle}{Expecting at max two answers with truefalse type} +% \fi + \setcounter{moodle@numcorrectanswers}{0}% Here this counter is for "questions" (items on the left column) + \loopthroughitemswithcommand{\moodle@print@matching@answer}% + \ifnum\c@numgathereditems<3% + \PackageWarning{moodle}{Moodle expects at least three "answers" with matching type} + \fi + \ifnum\c@moodle@numcorrectanswers<2% + \PackageWarning{moodle}{Moodle expects at least two "questions" with the matching type} + \fi + \ifmoodle@handout + \ifmoodle@shuffle + \let\moodle@matching@loop=\ForEachRandomItem + \else + \let\moodle@matching@loop=\ForEachFirstItem + \fi + \moodle@matching@loop{questionlist}{Question}{ + \xdef\test@i{\Question}% + \ExtractRandomItem{answerlist}{Answer}% + \ifx\test@i\@empty + \xdef\matching@table@text{\expandonce\matching@table@text & & $\bullet$~\expandonce\Answer\\}% + \else + \xdef\matching@table@text{\expandonce\matching@table@text\expandonce\Question~$\bullet$ & & $\bullet$~\expandonce\Answer\\}% + \fi + }% + \fi + \begin{tabular}{@{}>{\raggedleft}p{.45\linewidth}p{.1\linewidth}p{.45\linewidth}@{}}% + \matching@table@text + \end{tabular}% + \par + \ifmoodle@handout\else + \ifx\moodle@generalfeedback\@empty\relax\else% + \fbox{\parbox{\linewidth}{\emph{\moodle@generalfeedback}}}% + \fi + \fi + \egroup +} + +\long\def\moodle@print@matching@answer#1{% + \moodle@print@matching@answer@int#1 \@rdelim +}% +\newcommand\moodle@print@matching@answer@int[1][]{% + \moodle@print@matching@answer@int@int +}% +\long\def\moodle@print@matching@answer@int@int#1\answer#2\@rdelim{% + %\typeout{\string#1 \answer \string#2}% + \def\test@i{#1}% + \trim@spaces@in\test@i + \ifmoodle@handout + \ifx\test@i\@empty + \InsertLastItem{questionlist}{\@empty}% + \else + \InsertLastItem{questionlist}{#1}% + \fi + \InsertLastItem{answerlist}{#2}% + \else + \ifx\test@i\@empty + \g@addto@macro\matching@table@text{&&$\bullet$~#2\\}% + \else + \stepcounter{moodle@numcorrectanswers}% + \xa\g@addto@macro\xa\matching@table@text\xa{\test@i~$\bullet$&\leavevmode\cleaders\hb@xt@.44em{\hss$\cdot$\hss}\hfill\kern\z@&$\bullet$~#2\\}% + \fi + \fi +}% + % SAVING ANSWERS TO MEMORY \long\def\savematchinganswer#1{% \bgroup - \savematchinganswer@int#1\moodle@answer@rdelim% + \savematchinganswer@int#1 \moodle@answer@rdelim% \passvalueaftergroup{\moodle@answers@xml}% \egroup }% \newcommand\savematchinganswer@int[1][]{% \setkeys{moodle}{#1}% - \xa\savematchinganswer@int@int\space% + \savematchinganswer@int@int%\space }% \long\def\savematchinganswer@int@int#1\answer#2\moodle@answer@rdelim{% + %\typeout{\string#1 \answer \string#2}% % Note that #1 may simply be \relax. \def\moodle@subquestiontext{#1}% \def\moodle@subanswertext{#2}% @@ -1854,9 +3590,12 @@ % \show\moodle@subanswertext \addto@xml[4]{\moodle@answers@xml}{ <answer><text>\moodle@subanswertext</text></answer>}% \fi - \ifx\moodle@feedback\@empty\relax\else - \addto@xml[4]{\moodle@answers@xml}{ <feedback format="html"><text><![CDATA[<p>\moodle@feedback</p>]]></text></feedback>}% - \fi +% \ifx\moodle@feedback\@empty\relax\else +% \trim@spaces@in\moodle@feedback +% \xa\converttohtmlmacro\xa\moodle@feedback@html\xa{\moodle@feedback}% +% \addto@xml[4]{\moodle@answers@xml}{ <feedback +%format="html"><text><![CDATA[<p>\moodle@feedback@html</p>]]></text></feedback>}% +% \fi \addto@xml[2]{\moodle@answers@xml}{</subquestion>}% }% @@ -1868,12 +3607,9 @@ \writetomoodle{<question type="matching">}% \fi \moodle@writecommondata% - \moodle@writesingle% +% \moodle@writesingle% %irrelevant for the matching type \moodle@writeshuffle% - \moodle@writeanswernumbering% - \moodle@writeanswers% - \writetomoodle{</question>}% -% \gdef\moodle@answ +% \moodle@writeanswernumbering% %irrelevant for the matching type }% % \end{macrocode} % @@ -1896,6 +3632,8 @@ % LATEX PROCESSING % SAVING ANSWERS TO MEMORY +\newif\ifmoodle@clozemode +\moodle@clozemodefalse \NewEnviron{cloze}[2][]{% \bgroup \setkeys{moodle}{default grade=1}% @@ -1903,9 +3641,30 @@ % A cloze question won't have any \item's in it, so we just use \BODY. \moodle@enableclozeenvironments %First, the LaTeX processing. - \item \textbf{\moodle@questionname}\par + \item \textbf{\moodle@questionname} + \ifmoodle@handout + \moodle@makelatextag@qtype{cloze} + \else + \xa\xdef\xa\test@iii\xa{\moodle@tags}% + \ifx\test@iii\@moodle@empty\relax\else + \hfill tags: \texttt{\moodle@tags} + \fi + \par + \noindent + \moodle@makelatextag@qtype{cloze} + \moodle@makelatextag@value{default grade}{\moodle@marks{\csname moodle@default grade\endcsname}} + \moodle@makelatextag@value{penalty}{penalty} + \fi + \par \noindent \BODY + \edef\moodle@generalfeedback{\expandonce\moodle@feedback} + %\csname moodle@cloze@latexprocessing\endcsname + \ifmoodle@handout\else + \ifx\moodle@generalfeedback\@empty\relax\else% + \fbox{\parbox{\linewidth}{\emph{\moodle@generalfeedback}}}% + \fi + \fi %Now, writing information to memory. \@moodle@ifgeneratexml{% \xa\questiontext\xa{\BODY}% Save the question text as HTML. @@ -1927,6 +3686,7 @@ \gdef\writeclozequestion{% \writetomoodle{<question type="cloze">}% \moodle@writecommondata% + \moodle@writetags% \writetomoodle{</question>}% }% % \end{macrocode} @@ -1934,7 +3694,6 @@ % \subsubsection{Cloze Multiple Choice Questions} % % \begin{macrocode} -% TODO: Guard against [single=false] option \NewEnviron{clozemulti}[1][]{% \bgroup @@ -1947,40 +3706,88 @@ \def\moodle@clozemulti@output{}% \xa\g@addto@macro\xa\moodle@clozemulti@output\xa{\moodle@questionheader}% \def\clozemulti@coding{}% - \edef\clozemulti@coding{\otherlbrace\csname moodle@default grade\endcsname:}% + \edef\clozemulti@coding{\csname moodle@default grade\endcsname:}% + \ifmoodle@multiple + \PackageWarning{moodle}{Cloze Multiresponse only supported by Moodle 3.5+} + \g@addto@macro{\clozemulti@coding}{MULTIRESPONSE}% + \else + \g@addto@macro{\clozemulti@coding}{MULTICHOICE}% + \fi \ifcase\moodle@multi@mode\relax - % Case 0: dropdown box style - \g@addto@macro{\clozemulti@coding}{MULTICHOICE:}% + % Case 0: dropdown box style + \ifmoodle@shuffle + \g@addto@macro{\clozemulti@coding}{_}% + \fi \or % Case 1: vertical style - \g@addto@macro{\clozemulti@coding}{MULTICHOICE_V:}% + \ifmoodle@multiple + \PackageError{moodle}{Vertical mode (dropdown box) incompatible with multiresponse.} + \else + \g@addto@macro{\clozemulti@coding}{_V}% + \fi \else % Case 2: horizontal radio buttons - \g@addto@macro{\clozemulti@coding}{MULTICHOICE_H:}% + \g@addto@macro{\clozemulti@coding}{_H}% + \fi + \ifmoodle@shuffle + \PackageWarning{moodle}{Cloze Multi Shuffling only supported by Moodle 3.0+} + \g@addto@macro{\clozemulti@coding}{S:}% + \else + \g@addto@macro{\clozemulti@coding}{:}% \fi \bgroup \setkeys{moodle}{feedback={}}% \loopthroughitemswithcommand{\saveclozemultichoiceanswer}% \egroup - \xa\g@addto@macro\xa\clozemulti@coding\xa{\otherrbrace}% - \xa\g@addto@macro\xa\moodle@clozemulti@output\xa{\clozemulti@coding}% + %\xa\g@addto@macro\xa\clozemulti@coding\xa{\clozerbrace}% + \xa\g@addto@macro\xa\moodle@clozemulti@output\xa{\xa\clozelbrace\clozemulti@coding\clozerbrace}% + %\show\moodle@clozemulti@output \xa\gdef\xa\htmlize@afteraction@hook\xa{\moodle@clozemulti@output}% \def\endclozemulti@code{\htmlize@patchendenvironment}% \else %LaTeX version \moodle@questionheader% %Any introductory text just continues to be typeset. + \par + \noindent + \moodle@makelatextag@qtype{multi} + \ifmoodle@handout\else + \moodle@makelatextag@value{default grade}{\moodle@marks{\csname moodle@default grade\endcsname}} + \moodle@makelatextag@multi + \fi \def\cloze@multichoice@table@text{}% + \ifmoodle@handout\NewList{answerlist}\fi + %\let\moodle@feedback=\@empty \loopthroughitemswithcommand{\moodle@print@clozemultichoice@answer}% + \ifmoodle@handout + \ifmoodle@shuffle + \let\moodle@clozemult@loop=\ForEachRandomItem + \else + \let\moodle@clozemult@loop=\ForEachFirstItem + \fi + \moodle@clozemult@loop{answerlist}{Answer}{ + \xdef\cloze@multichoice@table@text{\expandonce\cloze@multichoice@table@text\expandonce\Answer}% + }% + \fi \ifcase\moodle@multi@mode\relax %Case 0: dropdown box style - \begin{tabular}[t]{|l|}\firsthline% (\firsthline is from the array package.) + \par\noindent + \ifmoodle@handout + \begin{tabular}[t]{|p{.45\linewidth}|} + \else + \begin{tabular}[t]{|p{.45\linewidth}|p{.45\linewidth}|} +% answer & feedback \\\hline\hline + \fi + \firsthline% (\firsthline is from the array package.) \cloze@multichoice@table@text% \end{tabular}% + \par% \or %Case 1: vertical style + \par\noindent \begin{itemize}\setlength\itemsep{0pt}\setlength\parskip{0pt}% \cloze@multichoice@table@text% \end{itemize}% + \par% \else %Case 2: horizontal radio buttons \par{\cloze@multichoice@table@text}\par% @@ -1994,40 +3801,75 @@ \def\moodle@print@clozemultichoice@answer#1{% - \moodle@print@clozemultichoice@answer@int#1\@rdelim% + \let\moodle@feedback=\@empty + \moodle@print@clozemultichoice@answer@int#1 \@rdelim% }% \newcommand\moodle@print@clozemultichoice@answer@int[1][]{% + \setkeys{moodle}{fraction=0,#1}% \moodle@print@clozemultichoice@answer@int@int% }% \def\moodle@print@clozemultichoice@answer@int@int#1#2\@rdelim{% + \def\moodle@answertext{}% % Case 0: "(answer) \\ \hline" % Case 1: "\item (answer)" % Case 2: "$\bullet~$(answer)\hfill" \ifcase\moodle@multi@mode\relax \relax% Case 0 \or - \g@addto@macro\cloze@multichoice@table@text{\item}% Case 1 + \g@addto@macro\moodle@answertext{\item}% Case 1 \else - \g@addto@macro\cloze@multichoice@table@text{$\bullet~$}% Case 2 + \g@addto@macro\moodle@answertext{$\bullet~$}% Case 2 \fi \def\test@i{#1}% \ifx\test@i\@star - \g@addto@macro\cloze@multichoice@table@text{#2$~\checkmark$}% + \setkeys{moodle}{fraction=100}% + \g@addto@macro\moodle@answertext{#2}% \else - \g@addto@macro\cloze@multichoice@table@text{#1#2}% + \g@addto@macro\moodle@answertext{#1#2}% + \fi + \trim@spaces@in\moodle@answertext + \trim@spaces@in\moodle@answertext + \ifmoodle@handout\else + \ifx\moodle@fraction\@hundred + \g@addto@macro\moodle@answertext{$~\checkmark$}% + \else + \moodle@checkfraction + \ifdim0pt=\moodle@fraction pt\relax\else + \xdef\moodle@answertext{\expandonce\moodle@answertext$~(\moodle@fraction\%)$}% + \fi + \fi \fi \ifcase\moodle@multi@mode\relax - \g@addto@macro{\cloze@multichoice@table@text}{\\ \hline}% Case 0 - \or - \relax% Case 1 + % Case 0 + \ifmoodle@handout\else + \xdef\moodle@answertext{\expandonce\moodle@answertext &\expandonce\emph{\expandonce\moodle@feedback}}% + \fi + \g@addto@macro\moodle@answertext{\\\hline} + \or % Case 1 + \ifmoodle@handout\else + \ifx\moodle@feedback\@empty\relax\else + \xdef\moodle@answertext{\expandonce\moodle@answertext \hfill \expandonce\emph{$\rightarrow$ \expandonce\moodle@feedback}}% + \fi + \fi + \else % otherwise + \ifmoodle@handout\else + \ifx\moodle@feedback\@empty\relax\else + \xdef\moodle@answertext{\expandonce\moodle@answertext\,\expandonce\emph{$\rightarrow$ \expandonce\moodle@feedback}}% + \fi + \fi + \g@addto@macro\moodle@answertext{\hfill}% Case 2 + \fi + \ifmoodle@handout + \def\temp{\InsertLastItem{answerlist}}% + \xa\temp\xa{\moodle@answertext}% \else - \g@addto@macro{\cloze@multichoice@table@text}{\hfill}% Case 2 + \xdef\cloze@multichoice@table@text{\expandonce\cloze@multichoice@table@text\expandonce\moodle@answertext}% \fi }% \def\saveclozemultichoiceanswer#1{% \bgroup - \saveclozemultichoiceanswer@int#1\moodle@answer@rdelim + \saveclozemultichoiceanswer@int#1 \moodle@answer@rdelim \egroup }% \newcommand\saveclozemultichoiceanswer@int[1][]{% @@ -2046,17 +3888,18 @@ \def\moodle@answertext{#1#2}% \fi \trim@spaces@in\moodle@answertext + \trim@spaces@in\moodle@answertext \ifx\moodle@fraction\@hundred \g@addto@macro\clozemulti@coding{=}% - \fi - \ifnum0<\moodle@fraction\relax - \ifnum\moodle@fraction<100\relax + \else + \moodle@checkfraction + \ifdim0pt=\moodle@fraction pt\relax\else \xdef\clozemulti@coding{\expandonce\clozemulti@coding\otherpercent\moodle@fraction\otherpercent}% \fi \fi \xdef\clozemulti@coding{\expandonce\clozemulti@coding\expandonce\moodle@answertext}% \ifx\moodle@feedback\@empty\else - \xdef\clozemulti@coding{\expandonce\clozemulti@coding\otherbackslash\otherhash\moodle@feedback}% + \xdef\clozemulti@coding{\expandonce\clozemulti@coding\otherbackslash\otherhash\expandonce\moodle@feedback}% \fi }% % \end{macrocode} @@ -2075,23 +3918,35 @@ \def\moodle@clozenumerical@output{}% \xa\g@addto@macro\xa\moodle@clozenumerical@output\xa{\moodle@questionheader}% \def\clozenumerical@coding{}% - \edef\clozenumerical@coding{\otherlbrace\csname moodle@default grade\endcsname:NUMERICAL:}% + \edef\clozenumerical@coding{\csname moodle@default grade\endcsname:NUMERICAL:}% \bgroup \setkeys{moodle}{feedback={}}% \loopthroughitemswithcommand{\saveclozenumericalanswer}% \egroup - \xa\g@addto@macro\xa\clozenumerical@coding\xa{\otherrbrace}% - \xa\g@addto@macro\xa\moodle@clozenumerical@output\xa{\clozenumerical@coding}% + %\xa\g@addto@macro\xa\clozenumerical@coding\xa{\otherrbrace}% + \xa\g@addto@macro\xa\moodle@clozenumerical@output\xa{\xa\clozelbrace\clozenumerical@coding\clozerbrace}% \xa\gdef\xa\htmlize@afteraction@hook\xa{\moodle@clozenumerical@output}% \def\endclozenumerical@code{\htmlize@patchendenvironment}% \else %LaTeX version \moodle@questionheader% %Any introductory text just continues to be typeset. - \def\cloze@numerical@table@text{}% - \loopthroughitemswithcommand{\moodle@print@clozenumerical@answer}% - \begin{tabular}[t]{|ll|}\firsthline - \cloze@numerical@table@text\hline% - \end{tabular}% + \par + \noindent + \moodle@makelatextag@qtype{numerical} + \ifmoodle@handout\else + \moodle@makelatextag@value{default grade}{\moodle@marks{\csname moodle@default grade\endcsname}} + \moodle@makelatextag@numerical + \par + \noindent + \def\cloze@numerical@table@text{}% + \loopthroughitemswithcommand{\moodle@print@clozenumerical@answer}% + \begin{tabular}[t]{|p{.45\linewidth}|p{.45\linewidth}|} + \firsthline% (\firsthline is from the array package.) +% answer & feedback \\\hline\hline + \cloze@numerical@table@text% + \end{tabular}% + \par% + \fi \def\endclozenumerical@code{\relax}% \fi \passvalueaftergroup\endclozenumerical@code% @@ -2100,6 +3955,7 @@ }[\endclozenumerical@code]% \def\moodle@print@clozenumerical@answer#1{% + \let\moodle@feedback=\@empty \bgroup \moodle@print@clozenumerical@answer@int#1\@rdelim \egroup @@ -2110,17 +3966,23 @@ }% \def\moodle@print@clozenumerical@answer@int@int#1\@rdelim{% \ifx\moodle@fraction\@hundred - \def\moodle@clozenumericalprint@fraction{$\checkmark$}% + \def\moodle@clozenumericalprint@fraction{$~\checkmark$}% \else - \edef\moodle@clozenumericalprint@fraction{(\moodle@fraction\%)}% + \moodle@checkfraction + \edef\moodle@clozenumericalprint@fraction{$(~\moodle@fraction\%)$}% \fi - \ifdim0pt=\moodle@tolerance pt\relax + \ifnum\z@=\moodle@tolerance \def\moodle@clozenumericalprint@tolerance{}% \else - \edef\moodle@clozenumericalprint@tolerance{\noexpand\pm\moodle@tolerance}% + \edef\moodle@clozenumericalprint@tolerance{$\,\pm\,$\moodle@printnum{\moodle@tolerance}}% \fi - \xdef\moodle@clozenumericalprint@line{$#1\moodle@clozenumericalprint@tolerance$ & \moodle@clozenumericalprint@fraction}% - \xa\g@addto@macro\xa\cloze@numerical@table@text\xa{\moodle@clozenumericalprint@line \\}% + \xdef\test@i{\trim@spaces{#1}}% + \ifx\test@i\@star + \xdef\moodle@clozenumericalprint@line{#1~\moodle@clozenumericalprint@fraction & \expandonce\emph{\expandonce\moodle@feedback}}% + \else + \xdef\moodle@clozenumericalprint@line{\moodle@printnum{#1}\moodle@clozenumericalprint@tolerance~\moodle@clozenumericalprint@fraction & \expandonce\emph{\expandonce\moodle@feedback}}% + \fi + \xa\g@addto@macro\xa\cloze@numerical@table@text\xa{\moodle@clozenumericalprint@line \\\hline}% }% @@ -2142,15 +4004,20 @@ \trim@spaces@in\moodle@answertext \ifx\moodle@fraction\@hundred \g@addto@macro\clozenumerical@coding{=}% - \fi - \ifnum0<\moodle@fraction\relax - \ifnum\moodle@fraction<100\relax + \else + \moodle@checkfraction + \ifdim0pt=\moodle@fraction pt\relax\else \xdef\clozenumerical@coding{\expandonce\clozenumerical@coding\otherpercent\moodle@fraction\otherpercent}% \fi \fi - \xdef\clozenumerical@coding{\expandonce\clozenumerical@coding\moodle@answertext:\moodle@tolerance}% + \ifx\moodle@answertext\@star + \xdef\clozenumerical@coding{\expandonce\clozenumerical@coding\moodle@answertext}% + \else + \xdef\clozenumerical@coding{\expandonce\clozenumerical@coding\moodle@answertext:\moodle@tolerance}% + \fi \ifx\moodle@feedback\@empty\else - \xdef\clozenumerical@coding{\expandonce\clozenumerical@coding\otherbackslash\otherhash\moodle@feedback}% + %\trim@spaces@in\moodle@feedback + \xdef\clozenumerical@coding{\expandonce\clozenumerical@coding\otherbackslash\otherhash\expandonce\moodle@feedback}% \fi }% % \end{macrocode} @@ -2177,29 +4044,38 @@ \xa\g@addto@macro\xa\moodle@clozeshortanswer@output\xa{\moodle@questionheader}% \def\clozeshortanswer@coding{}% \ifnum\count0=1\relax - \edef\clozeshortanswer@coding{\otherlbrace\csname moodle@default grade\endcsname:SHORTANSWER_C:}% + \edef\clozeshortanswer@coding{\csname moodle@default grade\endcsname:SHORTANSWER_C:}% \else - \edef\clozeshortanswer@coding{\otherlbrace\csname moodle@default grade\endcsname:SHORTANSWER:}% + \edef\clozeshortanswer@coding{\csname moodle@default grade\endcsname:SHORTANSWER:}% \fi \bgroup \setkeys{moodle}{feedback={}}% \loopthroughitemswithcommand{\saveclozeshortansweranswer}% \egroup - \xa\g@addto@macro\xa\clozeshortanswer@coding\xa{\otherrbrace}% - \xa\g@addto@macro\xa\moodle@clozeshortanswer@output\xa{\clozeshortanswer@coding}% + %\xa\g@addto@macro\xa\clozeshortanswer@coding\xa{\otherrbrace}% + \xa\g@addto@macro\xa\moodle@clozeshortanswer@output\xa{\xa\clozelbrace\clozeshortanswer@coding\clozerbrace}% \xa\gdef\xa\htmlize@afteraction@hook\xa{\moodle@clozeshortanswer@output}% \def\endclozeshortanswer@code{\htmlize@patchendenvironment}% \else %LaTeX version \moodle@questionheader% %Any introductory text just continues to be typeset. - \def\cloze@shortanswer@table@text{}% - \loopthroughitemswithcommand{\moodle@print@clozeshortanswer@answer}% - \ifnum\count0=1\relax - \marginpar{\tiny\mbox{(Case-Sensitive)}}% + \par + \noindent + \moodle@makelatextag@qtype{shortanswer} + \ifmoodle@handout\else + \moodle@makelatextag@value{default grade}{\moodle@marks{\csname moodle@default grade\endcsname}} + \moodle@makelatextag@shortanswer + \par + \noindent + \def\cloze@shortanswer@table@text{}% + \loopthroughitemswithcommand{\moodle@print@clozeshortanswer@answer}% + \begin{tabular}[t]{|p{.45\linewidth}|p{.45\linewidth}|} + \firsthline% (\firsthline is from the array package.) +% answer & feedback \\\hline\hline + \cloze@shortanswer@table@text% + \end{tabular}% + \par% \fi - \begin{tabular}[t]{|ll|}\firsthline - \cloze@shortanswer@table@text\hline% - \end{tabular}% \def\endclozeshortanswer@code{\relax}% \fi \passvalueaftergroup\endclozeshortanswer@code% @@ -2208,6 +4084,7 @@ }[\endclozeshortanswer@code]% \def\moodle@print@clozeshortanswer@answer#1{% + \let\moodle@feedback=\@empty \bgroup \moodle@print@clozeshortanswer@answer@int#1\@rdelim \egroup @@ -2218,12 +4095,13 @@ }% \def\moodle@print@clozeshortanswer@answer@int@int#1\@rdelim{% \ifx\moodle@fraction\@hundred - \def\moodle@clozeshortanswerprint@fraction{$\checkmark$}% + \def\moodle@clozeshortanswerprint@fraction{$~\checkmark$}% \else - \edef\moodle@clozeshortanswerprint@fraction{(\moodle@fraction\%)}% + \moodle@checkfraction + \edef\moodle@clozeshortanswerprint@fraction{$~(\moodle@fraction\%)$}% \fi - \xdef\moodle@clozeshortanswerprint@line{#1 & \moodle@clozeshortanswerprint@fraction}% - \xa\g@addto@macro\xa\cloze@shortanswer@table@text\xa{\moodle@clozeshortanswerprint@line \\}% + \xdef\moodle@clozeshortanswerprint@line{#1~\moodle@clozeshortanswerprint@fraction & \expandonce\emph{\expandonce\moodle@feedback}}% + \xa\g@addto@macro\xa\cloze@shortanswer@table@text\xa{\moodle@clozeshortanswerprint@line \\\hline}% }% @@ -2245,15 +4123,15 @@ \trim@spaces@in\moodle@answertext \ifx\moodle@fraction\@hundred \g@addto@macro\clozeshortanswer@coding{=}% - \fi - \ifnum0<\moodle@fraction\relax - \ifnum\moodle@fraction<100\relax + \else + \moodle@checkfraction + \ifdim0pt=\moodle@fraction pt\relax\else \xdef\clozeshortanswer@coding{\expandonce\clozeshortanswer@coding\otherpercent\moodle@fraction\otherpercent}% \fi \fi \xdef\clozeshortanswer@coding{\expandonce\clozeshortanswer@coding\moodle@answertext}% \ifx\moodle@feedback\@empty\else - \xdef\clozeshortanswer@coding{\expandonce\clozeshortanswer@coding\otherbackslash\otherhash\moodle@feedback}% + \xdef\clozeshortanswer@coding{\expandonce\clozeshortanswer@coding\otherbackslash\otherhash\expandonce\moodle@feedback}% \fi }% % \end{macrocode} @@ -2270,15 +4148,20 @@ \catcode`\&=12\gdef\otherampersand{&}% \catcode`\^=12\gdef\othercaret{^}% \catcode`\$=12\gdef\otherdollar{$}% - \catcode`\%=12\gdef\otherpercent{%}}% + \catcode`\%=12\gdef\otherpercent{%} + \catcode`\%=12\gdef\otherlbracket{[} + \catcode`\%=12\gdef\otherrbracket{]}}% {\catcode`\ =12\gdef\otherspace{ }}% -{\tt\catcode`\|=0\catcode`\\=12\relax|gdef|otherbackslash{\}}% +{\ttfamily\catcode`\|=0\catcode`\\=12\relax|gdef|otherbackslash{\}}% {\catcode`\[=1\catcode`\]=2\catcode`\{=12\catcode`\}=12% - \gdef\otherlbrace[{]\gdef\otherrbrace[}]] - + \gdef\otherlbrace[{]\gdef\otherrbrace[}]\gdef\clozelbrace[{]\gdef\clozerbrace[}]]% \edef\@otherlbrace{\otherlbrace}% \edef\@otherrbrace{\otherrbrace}% +\edef\@otherlbracket{\otherlbracket}% +\edef\@otherrbracket{\otherrbracket}% +\edef\@clozelbrace{\clozelbrace}% +\edef\@clozerbrace{\clozerbrace}% \edef\@otherdollar{\otherdollar}% \edef\@otherbackslash{\otherbackslash}% \edef\@othertilde{\othertilde}% @@ -2381,9 +4264,16 @@ % \end{macrocode} % % \subsection{Level-Tracking} +% +% \subsubsection{TeX groups} +% While parsing, we'll need to keep track of how deeply nested in \TeX\ groups we are. +% \begin{macrocode} +\newcount\grouplevel +% \end{macrocode} +% +% \subsubsection{Math mode} % While parsing, we'll need to keep track of whether -% we are in math mode (and how many levels deep the math mode might be nested), -% and also how deeply nested in \TeX\ groups we are. +% we are in math mode (and how many levels deep the math mode might be nested). % \begin{macrocode} \newcount\moodle@mathmodedepth \moodle@mathmodedepth=0\relax @@ -2393,12 +4283,15 @@ \else #2% \fi -} - -\newcount\grouplevel +}% +% \end{macrocode} +% +% \subsubsection{Nested Lists} +% While parsing, we'll need to keep track levels of nested list. +% \begin{macrocode} +\newcount\moodle@listdepth +\moodle@listdepth=0\relax -\newif\ifhtmlizer@active -\htmlizer@activefalse % \end{macrocode} % % \subsection{Separation} @@ -2436,6 +4329,8 @@ % \subsection{Main Code: the HTMLizer} % % \begin{macrocode} +\newif\ifhtmlizer@active +\htmlizer@activefalse \newif\ifhtmlize@actioncs \newif\ifhtmlize@expandcs \newif\ifhtmlize@passcs @@ -2456,7 +4351,7 @@ \advance\grouplevel by 1\relax \bgroup \ultradetokenize@to@macro\htmlize@texttoscan{#1}% - \xa\htmlize@recursive@i\htmlize@texttoscan\@htmlize@stop\@htmlize@stop\@htmlize@stop\htmlize@rdelim@i + \xa\htmlize@recursive@i\htmlize@texttoscan\@htmlize@stop\@htmlize@stop\@htmlize@stop\htmlize@rdelim@i% \egroup \advance\grouplevel by -1\relax }% @@ -2502,7 +4397,13 @@ \gdef\htmlize@scrap{\converttohtml@int}% \xa\g@addto@macro\xa\htmlize@scrap\xa{\xa{\htmlize@blockinbraces}}% \g@addto@macro\htmlize@scrap{\g@addto@macro\htmlize@output}% - \xa\g@addto@macro\xa\htmlize@scrap\xa{\xa{\otherrbrace}}% + \ifmoodle@clozemode + \xa\g@addto@macro\xa\htmlize@scrap\xa{\xa{\otherbackslash\otherrbrace}}% +% \moodle@ifmathmode{\xa\g@addto@macro\xa\htmlize@scrap\xa{\xa{\otherbackslash}}}% +% {}% + \else + \xa\g@addto@macro\xa\htmlize@scrap\xa{\xa{\otherrbrace}}% + \fi \g@addto@macro\htmlize@scrap{\htmlize@recursive@i}% \xa\g@addto@macro\xa\htmlize@scrap\xa{\htmlize@remainder@detokenized\@htmlize@stop\@htmlize@stop\@htmlize@stop\htmlize@rdelim@i}% % Okay, that's done. It's stored in a global macro. @@ -2519,10 +4420,10 @@ \htmlize@displaymathshift@replace#1#2#3\htmlize@rdelim@iii% \else % Single dollar sign, so we're entering inline math mode. - % We grab everything between $$...$$, sanitize it, and add it verbatim to + % We grab everything between $...$, sanitize it, and add it verbatim to % our output. \htmlize@inlinemathshift@replace#1#2#3\htmlize@rdelim@iii% - \fi + \fi% \ifx\test@ii\@otherdollar % Now we resume work. % The \htmlize@xxxxxxmathshift@replace macro stored the remaining text in \htmlize@remaining@text. % Note that since we never detokenized and retokenized #1#2#3, @@ -2647,9 +4548,9 @@ %\edef\ds{Must pass on \xa\string\@htmlize@cs}\show\ds \g@addto@macro\htmlize@output{#1}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% - \fi - \fi - \fi + \fi% \ifhtmlize@expandcs + \fi% \ifhtmlize@actioncs + \fi% \ifx\@htmlize@cs\@@begin@cs \else% \ifx\test@i\@othertilde% % The ~ becomes non-breaking space @@ -2664,7 +4565,7 @@ \else \g@addto@macro\htmlize@output{\otherampersand lsquo;}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% - \fi + \fi% \ifx\test@ii\@lsinglequote% \else \ifx\test@i\@rsinglequote% \ifx\test@ii\@rsinglequote% @@ -2674,7 +4575,7 @@ \else \g@addto@macro\htmlize@output{\otherampersand rsquo;}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% - \fi + \fi% \ifx\test@ii\@rsinglequote% \else \ifx\test@i\@doublequote \g@addto@macro\htmlize@output{\otherampersand rdquo;}% @@ -2689,20 +4590,20 @@ \moodle@ifmathmode{\g@addto@macro\htmlize@output{\otherampersand gt;}}% {\g@addto@macro\htmlize@output{>}}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% - \else + \else % Default case: write first token to output, call self on remaining tokens. \g@addto@macro\htmlize@output{#1}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% - \fi - \fi - \fi - \fi - \fi - \fi - \fi - \fi - \fi - \fi + \fi% \ifx\test@i\@gt + \fi% \ifx\test@i\@lt + \fi% \ifx\test@i\@doublequote + \fi% \ifx\test@i\@rsinglequote% + \fi% \ifx\test@i\@lsinglequote% + \fi% \ifx\test@i\@othertilde% + \fi% \ifx\test@i\@otherbackslash% + \fi% \ifx\test@i\@otherdollar% + \fi% \ifx\test@i\@otherlbrace% + \fi% \ifx\test@i\@@htmlize@stop \htmlize@next@i }% @@ -2721,16 +4622,38 @@ \edef\inlinemathleftdelim{\otherbackslash(}% \def\inlinemathrightdelim#1{\advancemathmodecounter{-1}% \g@addto@macro\htmlize@output{\otherbackslash)}}% -\edef\displaymathleftdelim{<CENTER>\otherbackslash[}% +\edef\displaymathleftdelim{\otherbackslash[}% \def\displaymathrightdelim#1{\advancemathmodecounter{-1}% - \g@addto@macro\htmlize@output{\otherbackslash]</CENTER>}}% + \g@addto@macro\htmlize@output{\otherbackslash]}}% +\long\def\htmlize@inlinemath@recursive@i#1#2#3\htmlize@rdelim@i{% + % inspired by \htmlize@recursive@i + \def\test@i{#1}% + \ifx\test@i\@@htmlize@stop + \let\htmlize@next@i=\relax + \else + \ifx\test@i\@otherbackslash + \g@addto@macro\mathtext{#1#2}% + \def\htmlize@next@i{\xa\htmlize@inlinemath@recursive@i#3\htmlize@rdelim@i}% + \else + \ifx\test@i\@otherdollar + \let\htmlize@next@i=\relax + \g@addto@macro\aftertext{#2#3}% + \else + \g@addto@macro\mathtext{#1}% + \def\htmlize@next@i{\xa\htmlize@inlinemath@recursive@i#2#3\htmlize@rdelim@i}% + \fi + \fi + \fi + \htmlize@next@i +} {\catcode`\$=12\relax% - \gdef\htmlize@inlinemathshift@replace$#1$#2\htmlize@rdelim@iii{% + \gdef\htmlize@inlinemathshift@replace#1#2\htmlize@rdelim@iii{% %\def\ds{inline math shift has '#1' and '#2'}\show\ds \xa\g@addto@macro\xa\htmlize@output\xa{\inlinemathleftdelim}% \advancemathmodecounter{1}% - \def\mathtext{#1}% - \def\aftertext{#2}% + \gdef\mathtext{}% + \gdef\aftertext{}% + \htmlize@inlinemath@recursive@i#2\htmlize@rdelim@i% \xdef\htmlize@remaining@text{\expandonce\mathtext% \otherbackslash inlinemathrightdelim{}% \expandonce\aftertext}% @@ -2746,6 +4669,7 @@ \expandonce\aftertext}% }% } + % \end{macrocode} % % \subsection{Engines for Control Sequences} @@ -2847,6 +4771,25 @@ \xa\g@addto@macro\xa\htmlize@activate@css\xa{\htmlize@scrap}% \xa\def\csname html@\string#1\endcsname% %And this \def\html@\oldcsname is follows by the remainder of the definition. } +\def\html@action@newcommand#1[#2][#3]#4{% + %\message{>>> Defining #1[#2][#3]{...} ^^J} + \ifmoodle@draftmode + \else + \xa\html@action@def\csname #1\endcsname{\csname moodle@#1@int\endcsname}% + \fi + % Note that \htmlize@do@actioncs will 'patch' this by putting + % '\xa\htmlize@proceedwiththerest\htmlize@afteraction@hook' + % at the end. We want those 3 tokens to occur instead after + % the graphics filename. + \xa\csdef{moodle@#1@int}##1##2##3{\csname moodle@#1@int@int\endcsname}% + % This gobbles up those three spurious tokens, + % which we will re-insert after our work is done. + \xa\newcommand\csname moodle@#1@int@int\endcsname[#2][#3]{% + #4% + % Now we re-insert the code to get the HTMLizing going again. + \xa\htmlize@proceedwiththerest\htmlize@afteraction@hook + }% +} % \end{macrocode} % % \subsubsection{Engine for expansion control sequences} @@ -2868,6 +4811,24 @@ } \let\htmlregister=\htmlize@record@expand % \end{macrocode} +% Often users define a list of macros at the end of the preamble. +% It can be cumbersome to record individually these macros for expansion. +% By calling |\moodleregisternewcommands| they trigger the automatic +% expansion of macros defined subsequently using |\newcommand|, +% |\renewcommand|, |\providecommand| or their starred variants. +% \begin{macrocode} +\def\moodleregisternewcommands{% + %% INSPIRED FROM + %https://tex.stackexchange.com/questions/73271/how-to-redefine-or-patch-the-newcommand-command + \newcommand*{\saved@ifdefinable}{} + \let\saved@ifdefinable\@ifdefinable + \renewcommand{\@ifdefinable}[2]{% + \saved@ifdefinable{##1}{##2}% + \htmlregister{##1} + }% + \let\@@ifdefinable\@ifdefinable +}% +% \end{macrocode} % On the other hand, if an alternate version of the macro is % needed for HTML purposes, you can define its HTML version with % |\html@def\mymacro...| @@ -2901,20 +4862,50 @@ \htmlize@record@environment{shortanswer} \html@newenvironment{center}{\xdef\htmlize@afteraction@hook{<CENTER>\expandonce\BODY</CENTER>}}% -\html@newenvironment{enumerate}{% - \xa\gatheritems\xa{\BODY}% - \gdef\htmlize@afteraction@hook{<OL>}% - \loopthroughitemswithcommand{\moodle@itemtoLI}% - \g@addto@macro\htmlize@afteraction@hook{</OL>}% -}% -\html@newenvironment{itemize}{% - \xa\gatheritems\xa{\BODY}% - \gdef\htmlize@afteraction@hook{<UL>}% - \loopthroughitemswithcommand{\moodle@itemtoLI}% - \g@addto@macro\htmlize@afteraction@hook{</UL>}% + +\def\moodle@save@getitems@state{% + \global\xa\xdef\csname moodle@currentitemnumber@level@\the\moodle@listdepth\xa\endcsname\xa{\thecurrentitemnumber}% + \global\xa\xdef\csname moodle@numgathereditems@level@\the\moodle@listdepth\xa\endcsname\xa{\thenumgathereditems}% + \moodle@saveitems{\thenumgathereditems}% +}% +\def\moodle@restore@getitems@state{% + \setcounter{numgathereditems}{\csname moodle@numgathereditems@level@\the\moodle@listdepth\endcsname}% + \setcounter{currentitemnumber}{\csname moodle@currentitemnumber@level@\the\moodle@listdepth\endcsname}% + \moodle@restoreitems{\thenumgathereditems}% +}% +\def\moodle@saveitems#1{% + \ifnum#1>0% + \global\csletcs{moodle@level@\the\moodle@listdepth @item@#1}{getitems@item@#1}% + \xa\moodle@saveitems\xa{\number\numexpr#1-1\expandafter}% + \fi +}% +\def\moodle@restoreitems#1{% + \ifnum#1>0% + \global\csletcs{getitems@item@#1}{moodle@level@\the\moodle@listdepth @item@#1}% + \global\xa\let\csname moodle@level@\the\moodle@listdepth @item@#1\endcsname=\@undefined + \xa\moodle@restoreitems\xa{\number\numexpr#1-1\expandafter}% + \fi +}% +\def\moodle@makelistenv#1#2{% + \html@newenvironment{#1}{% + \advance\moodle@listdepth by 1\relax + \moodle@save@getitems@state% + \xa\gatheritems\xa{\BODY}% + \gdef\htmlize@afteraction@hook{<#2>}% + \loopthroughitemswithcommand{\moodle@itemtoLI}% + \g@addto@macro\htmlize@afteraction@hook{</#2>}% + \moodle@restore@getitems@state% + \advance\moodle@listdepth by -1\relax + }% }% + +\moodle@makelistenv{enumerate}{OL}% +\moodle@makelistenv{itemize}{UL}% + \def\moodle@itemtoLI#1{% - \g@addto@macro\htmlize@afteraction@hook{<LI>#1</LI>}% + \g@addto@macro\htmlize@afteraction@hook{<LI>#1}% + \trim@spaces@in\htmlize@afteraction@hook% + \g@addto@macro\htmlize@afteraction@hook{</LI>}% }% % \end{macrocode} @@ -2925,11 +4916,15 @@ \def\advancemathmodecounter#1{% \global\advance\moodle@mathmodedepth by #1\relax } +\def\openclozemode{% + \global\moodle@clozemodetrue\relax +} +\def\endclozemode{% + \global\moodle@clozemodefalse\relax +} \htmlize@record@action{\advancemathmodecounter}% - - - - +\htmlize@record@action{\openclozemode}% +\htmlize@record@action{\endclozemode}% \htmlize@record@action{\relax}% \html@action@def\#{\g@addto@macro\htmlize@output{\otherhash}}% @@ -2942,15 +4937,32 @@ }% \html@action@def\}{% \moodle@ifmathmode{\g@addto@macro\htmlize@output{\otherbackslash\otherrbrace}}% - {\g@addto@macro\htmlize@output{\otherrbrace}}% + {\ifmoodle@clozemode\g@addto@macro\htmlize@output{\otherbackslash}\fi + \g@addto@macro\htmlize@output{\otherrbrace}}% + }% +\html@action@def\[{% + \advancemathmodecounter{1} + \g@addto@macro\htmlize@output{\otherbackslash\otherlbracket}% + }% +\html@action@def\]{% + \g@addto@macro\htmlize@output{\otherbackslash\otherrbracket}% + \advancemathmodecounter{-1} + }% +\html@action@def\({% + \advancemathmodecounter{1} + \g@addto@macro\htmlize@output{\otherbackslash(}% + }% +\html@action@def\){% + \g@addto@macro\htmlize@output{\otherbackslash)}% + \advancemathmodecounter{-1} }% \html@action@def\ldots{% \moodle@ifmathmode{\g@addto@macro\htmlize@output{\string\ldots}}% - {\g@addto@macro\htmlize@output{...}}% + {\g@addto@macro\htmlize@output{\otherampersand hellip\othersemicol}}% }% \html@action@def\dots{% \moodle@ifmathmode{\g@addto@macro\htmlize@output{\string\dots}}% - {\g@addto@macro\htmlize@output{...}}% + {\g@addto@macro\htmlize@output{\otherampersand hellip\othersemicol}}% }% \html@action@def\ {% \moodle@ifmathmode{\g@addto@macro\htmlize@output{\otherbackslash\otherspace}}% @@ -2959,12 +4971,25 @@ \html@action@def\${% \g@addto@macro\htmlize@output{\otherdollar}% }% - \html@action@def\clozetilde{% \xa\g@addto@macro\xa\htmlize@output\xa{\othertilde}% }% +\html@action@def\clozelbrace{% + \openclozemode + \xa\g@addto@macro\xa\htmlize@output\xa{\otherlbrace}% + }% +\html@action@def\clozerbrace{% + \xa\g@addto@macro\xa\htmlize@output\xa{\otherrbrace}% + \endclozemode + }% +\html@action@def\TeX{% + \g@addto@macro\htmlize@output{\otherbackslash(\otherbackslash rm\otherbackslash TeX\otherbackslash)} + }% +\html@action@def\LaTeX{% + \g@addto@macro\htmlize@output{\otherbackslash(\otherbackslash rm\otherbackslash LaTeX\otherbackslash)} + }% - +{\catcode`;=12\relax\gdef\othersemicol{;}} %Diacritical marks over vowels {\catcode`|=3\relax @@ -2974,32 +4999,40 @@ \g@addto@macro\htmlize@activate@css{% \def#1##1{% \ifinlist{##1}{\htmlize@vowels}% - {\g@addto@macro\htmlize@output{\otherampersand##1#2;}}% + {\g@addto@macro\htmlize@output{\otherampersand##1#2\othersemicol}}% {\xa\g@addto@macro\htmlize@output\xa{\string#1##1}}% }% }% } \htmlize@define@diacritic{\^}{circ}% \htmlize@define@diacritic{\'}{acute}% -\htmlize@define@diacritic{\"}{uml}% \htmlize@define@diacritic{\`}{grave}% +%Diaeresis/Tréma/Umlaut +{\catcode`|=3\relax + \gdef\htmlize@diaeresis{a|e|i|o|u|y|A|E|I|O|U|Y|}} +\html@action@def\"#1{% + \ifinlist{#1}{\htmlize@diaeresis}% + {\g@addto@macro\htmlize@output{\otherampersand#1uml\othersemicol}}% + {\xa\g@addto@macro\htmlize@output\xa{\string\"#1}}% +}% + %Hungarian long-umlaut diacritics \def\@o{o}\def\@O{O}\def\@u{u}\def\@U{U}% \html@action@def\H#1{% \bgroup \def\test@i{#1}% \ifx\test@i\@O - \def\toadd{\otherampersand\otherhash337;}% + \def\toadd{\otherampersand\otherhash336\othersemicol}% \else \ifx\test@i\@o - \def\toadd{\otherampersand\otherhash337;}% + \def\toadd{\otherampersand\otherhash337\othersemicol}% \else - \ifx\test@i\U - \def\toadd{\otherampersand\otherhash368;}% + \ifx\test@i\@U + \def\toadd{\otherampersand\otherhash368\othersemicol}% \else - \ifx\test@i\u - \def\toadd{\otherampersand\otherhash369;}% + \ifx\test@i\@u + \def\toadd{\otherampersand\otherhash369\othersemicol}% \else \def\toadd{\otherbackslash\otherlbrace#1\otherrbrace}% \fi @@ -3010,50 +5043,275 @@ \egroup }% +%Cedilla +\def\@c{c}\def\@C{C}\def\@s{s}\def\@S{S}\def\@t{t}\def\@T{T}% +\html@action@def\c#1{% + \bgroup + \def\test@i{#1}% + \ifx\test@i\@c + \def\toadd{\otherampersand ccedil\othersemicol}% + \else + \ifx\test@i\@C + \def\toadd{\otherampersand Ccedil\othersemicol}% + \else + \ifx\test@i\@s + \def\toadd{\otherampersand\otherhash351\othersemicol}% + \else + \ifx\test@i\@S + \def\toadd{\otherampersand\otherhash350\othersemicol}% + \else + \ifx\test@i\@t + \def\toadd{\otherampersand\otherhash355\othersemicol}% + \else + \ifx\test@i\@T + \def\toadd{\otherampersand\otherhash354\othersemicol}% + \else + \def\toadd{\otherbackslash\otherlbrace#1\otherrbrace}% + \fi + \fi + \fi + \fi + \fi + \fi + \xa\g@addto@macro\xa\htmlize@output\xa{\toadd}% + \egroup +}% %Tilde over a, n, o {\catcode`|=3\relax \gdef\htmlize@tilde{A|N|O|a|n|o|}} \html@action@def\~#1{% \ifinlist{#1}{\htmlize@tilde}% - {\g@addto@macro\htmlize@output{\otherampersand#1tilde;}}% + {\g@addto@macro\htmlize@output{\otherampersand#1tilde\othersemicol}}% {\xa\g@addto@macro\htmlize@output\xa{\string\~#1}}% - }% +}% + +% breve diacritics +{\catcode`|=3\relax + \gdef\htmlize@breve{A|G|U|a|g|u|}} +\def\@e{e}\def\@E{E}\def\@i{i}\def\@ii{\i}\def\@I{I}\def\@o{o}\def\@O{O}% +\html@action@def\u#1{% + \ifinlist{#1}{\htmlize@breve}% + {\g@addto@macro\htmlize@output{\otherampersand#1breve\othersemicol}}% + { + \bgroup + \def\test@i{#1}% + \ifx\test@i\@E + \def\toadd{\otherampersand\otherhash276\othersemicol}% + \else + \ifx\test@i\@e + \def\toadd{\otherampersand\otherhash277\othersemicol}% + \else + \ifx\test@i\@I + \def\toadd{\otherampersand\otherhash300\othersemicol}% + \else + \ifx\test@i\@i + \def\toadd{\otherampersand\otherhash301\othersemicol}% + \else + \ifx\test@i\@ii + \def\toadd{\otherampersand\otherhash301\othersemicol}% + \else + \ifx\test@i\@O + \def\toadd{\otherampersand\otherhash334\othersemicol}% + \else + \ifx\test@i\@o + \def\toadd{\otherampersand\otherhash335\othersemicol}% + \else + \def\toadd{\otherbackslash\otherlbrace#1\otherrbrace}% + \fi + \fi + \fi + \fi + \fi + \fi + \fi + \xa\g@addto@macro\xa\htmlize@output\xa{\toadd}% + \egroup + }% +}% + +% caron diacritics +{\catcode`|=3\relax + \gdef\htmlize@caron{C|D|E|L|N|R|S|T|Z|c|d|e|l|n|r|s|t|z|}} +\html@action@def\v#1{% + \ifinlist{#1}{\htmlize@caron}% + {\g@addto@macro\htmlize@output{\otherampersand#1caron\othersemicol}}% + {\xa\g@addto@macro\htmlize@output\xa{\string\v#1}}% +}% % \end{macrocode} % % \subsubsection{Command sequences for Expansion} % % \begin{macrocode} -\html@def\underline#1{<SPAN STYLE="text-decoration: underline;">#1</SPAN>} +\html@def\underline#1{<SPAN STYLE=\&\#34;text-decoration: underline;\&\#34;>#1</SPAN>} \html@def\emph#1{<EM>#1</EM>}% +\html@def\textit#1{<I>#1</I>}% \html@def\textbf#1{<B>#1</B>}% +\html@def\texttt#1{<CODE>#1</CODE>}% +\html@def\textsc#1{<SPAN STYLE=\&\#34;font-variant: small-caps;\&\#34;>#1</SPAN>} +\html@def\url#1{<A href=\&\#34;#1\&\#34;>#1</A>}% +\html@def\href#1#2{<A href=\&\#34;#1\&\#34;>#2</A>}% +\html@def\textsuperscript#1{<SUP>#1</SUP>}% +\html@def\up#1{<SUP>#1</SUP>}% +\html@def\fup#1{<SUP>#1</SUP>}% +\html@def\textsubscript#1{<SUB>#1</SUB>}% \html@def\blank{____________}% \html@def\par{</P><P>}% \html@def\aa{\å}% \html@def\AA{\Å}% \html@def\ae{\æ}% \html@def\AE{\Æ}% +\html@def\oe{\œ}% +\html@def\OE{\Œ}% \html@def\S{\§}% \html@def\euro{\€}% \html@def\texteuro{\€}% \html@def\o{\ø}% \html@def\O{\Ø}% \html@def\ss{\ß}% +\html@def\SS{\&\#7838;}% \html@def\l{\ł}% \html@def\L{\Ł}% +\html@def\og{\«\&\#8239;}% +\html@def\guillemotleft{\«\&\#8239;}% +\html@def\fg{\&\#8239;\»}% +\html@def\guillemotright{\&\#8239;\»}% +\html@def\,{\&\#8239;}% +\html@def\thinspace{\&\#8239;}% +\html@def\textbackslash{\&\#92;}% +\html@def\_{\&\#95;}% +\html@def\textquestiondown{\¿}% +\html@def\textexclamdown{\¡}% % AH CUSTOM MACROS TO EXPAND --- remove these before publishing! -\htmlize@record@expand{\inv}% -\htmlize@record@expand{\rec}% +%\htmlize@record@expand{\inv}% +%\htmlize@record@expand{\rec}% \htmlize@record@action\inlinemathrightdelim \htmlize@record@action\displaymathrightdelim - % \end{macrocode} % -% \subsection{Graphics via {\tt\string\includegraphics}} +% \subsection{Graphics via {\ttfamily\string\includegraphics}} % +% \subsubsection{Finding Media Files} +% The following code is adapted from the command |\Ginclude@graphics| as +% found in |graphics.sty|. +% Calling |\moodle@media@find|\marg{filename} looks for the file the +% same way |\includegraphics| does, with or without extension provided, +% in the current folder and in folders specified with |\graphicspath{}|. +% The allowed extensions and their relative priorities can be set +% via |\DeclareGraphicsExtensions{}|. +% The outcome is that the macros |\moodle@media@base| and |\moodle@media@ext| +% are set with the basename, including path, and extension, respectively. +% +% \begin{macrocode} +\def\moodle@media@find#1{% + \ifx\detokenize\@undefined\else + \edef\Gin@extensions{\detokenize\expandafter{\Gin@extensions}}% + \fi + \begingroup + \let\input@path\Ginput@path + \set@curr@file{#1}% + \expandafter\filename@parse\expandafter{\@curr@file}% + \ifx\filename@ext\Gin@gzext + \expandafter\filename@parse\expandafter{\filename@base}% + \ifx\filename@ext\relax + \let\filename@ext\Gin@gzext + \else + \edef\Gin@ext{\Gin@ext\Gin@sepdefault\Gin@gzext}% + \fi + \fi + \ifx\filename@ext\relax + \@for\Gin@temp:=\Gin@extensions\do{% + \ifx\Gin@ext\relax + \Gin@getbase\Gin@temp + \fi}% + \else + \Gin@getbase{\Gin@sepdefault\filename@ext}% + \ifnum0% + \ifx\Gin@ext\relax 1% + \else \@ifundefined{Gin@rule@\Gin@ext}{1}{0}% + \fi >0 + \let\Gin@ext\relax + \let\Gin@savedbase\filename@base + \let\Gin@savedext\filename@ext + \edef\filename@base{\filename@base\Gin@sepdefault\filename@ext}% + \let\filename@ext\relax + \@for\Gin@temp:=\Gin@extensions\do{% + \ifx\Gin@ext\relax + \Gin@getbase\Gin@temp + \fi}% + \ifx\Gin@ext\relax + \let\filename@base\Gin@savedbase + \let\filename@ext\Gin@savedext + \fi + \fi + \ifx\Gin@ext\relax + \@warning{File `#1' not found}% + \def\Gin@base{\filename@area\filename@base}% + \edef\Gin@ext{\Gin@sepdefault\filename@ext}% + \fi + \fi + \ifx\Gin@ext\relax + \@latex@error{File `#1' not found}% + {I could not locate the file with any of these extensions:^^J% + \Gin@extensions^^J\@ehc}% + \else + % begin modified part + \xdef\moodle@media@base{\detokenize\xa{\Gin@base}}% + \xdef\moodle@media@ext{\detokenize\xa{\Gin@ext}}% + % end modfied part + \fi + \endgroup +} +% \end{macrocode} +% \begin{macrocode} +%See +% * https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types +% * https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers +\def\moodle@media@formats{} +\newcommand{\DeclareMediaFormat}[3]{% + \xdef\moodle@media@formats{\ifx\moodle@media@formats\empty\else\moodle@media@formats,\fi#1}% + \csgdef{moodle@media@#1list}{#2}% + \csgdef{moodle@media@#1mime}{#3}% +}% +% Image Formats +\DeclareMediaFormat{PNG}{.png,.PNG}{image/png}% +\DeclareMediaFormat{JPEG}{.jpg,.JPG,.jpeg,.JPEG}{image/jpeg}% +\DeclareMediaFormat{SVG}{.svg,.SVG}{image/svg+xml}% +\DeclareMediaFormat{GIF}{.gif}{image/gif}% +% Audio Formats +\DeclareMediaFormat{WAV}{.wav,.WAV}{audio/wave}% +\DeclareMediaFormat{MP3}{.mp3,.MP3}{audio/mpeg}% +\DeclareMediaFormat{OGG}{.ogg,.opus,.og&}{audio/ogg}% +%\DeclareMediaFormat{FLAC}{.flac,.FLAC}{audio/flac}% +% Video Formats +\DeclareMediaFormat{WEBM}{.webm,.webm}{video/webm}% +\DeclareMediaFormat{MP4}{.mp4,.MP4,.m4v,.M4V}{video/mp4}% +\DeclareMediaFormat{OGV}{.ogv,.OGV}{video/ogg}% +% \end{macrocode} +% \begin{macrocode} +\def\moodle@media@mime@identify#1{% + \edef\test@i{\detokenize\xa{#1}}% + \let\moodle@media@mime@current\relax + \edef\moodle@media@formats{\detokenize\xa{\moodle@media@formats}}% + \@for\@format:=\moodle@media@formats\do{% + \edef\@templist{\csname moodle@media@\@format list\endcsname}% + \@for\@ext:=\@templist\do{% + \ifx\moodle@media@mime@current\relax + \edef\@ext{\detokenize\xa{\@ext}}% + \ifx\test@i\@ext + \message{<<extension \@ext (\@format) identified>>^^J}% + \xdef\moodle@media@mime@current{\csname moodle@media@\@format mime\endcsname}% + \xdef\moodle@media@ext@current{#1}% + \fi + \fi + }% + }% +} +% \end{macrocode} +% % \subsubsection{External program command lines} % We first set up commands for the external programs. % \begin{macrocode} @@ -3068,20 +5326,65 @@ \expandafter\def\htmlize@executable@macro{#1}% } -\def\ghostscriptcommand{\htmlize@setexecutable\gs} -\def\opensslcommand{\htmlize@setexecutable\openssl} -\def\imagemagickcommand{\htmlize@setexecutable\htmlize@imagemagick@convert} +\def\ghostscriptcommand{\htmlize@setexecutable\gs}% +\def\baselxivcommand{\htmlize@setexecutable\baselxiv}% +\def\imagemagickcommand{\htmlize@setexecutable\htmlize@imagemagick@convert}% +\def\optipngcommand{\htmlize@setexecutable\optipng}% +\def\PDFtoSVGcommand{\htmlize@setexecutable\PDFtoSVG}% +\def\SVGtoPDFcommand{\htmlize@setexecutable\SVGtoPDF}% +\def\optiSVGcommand{\htmlize@setexecutable\optiSVG}% +\def\DeleteFilecommand{\htmlize@setexecutable\DeleteFiles}% \ifwindows% - \ghostscriptcommand{gswin64c.exe}% + \ghostscriptcommand{gswin64c.exe -dBATCH -dNOPAUSE -sDEVICE=pngalpha}% + \baselxivcommand{certutil}% + \DeleteFilecommand{del}% \else% - \ghostscriptcommand{gs}% + \ghostscriptcommand{gs -dBATCH -dNOPAUSE -sDEVICE=pngalpha}% + \baselxivcommand{base64}% + \DeleteFilecommand{rm -f}% \fi% -\opensslcommand{openssl}% -\imagemagickcommand{convert}% +\imagemagickcommand{convert -colorspace RGB}% +\optipngcommand{optipng -clobber -strip all -quiet}% +\PDFtoSVGcommand{inkscape --export-type=svg --export-area-page --vacuum-defs}%--pdf-poppler +% remove the "--pdf-poppler" import option if you want to preserve text (avoid conversion to path) +\SVGtoPDFcommand{inkscape --export-type=pdf --export-area-page}% +\optiSVGcommand{scour -q --enable-id-stripping --enable-comment-stripping + --shorten-ids --indent=none --remove-descriptive-elements}% % \end{macrocode} % +% \subsubsection{Conversion and inclusion of non-native formats} +% \begin{macrocode} +\AtEndPreamble{% + \@ifpackageloaded{graphicx}{% + \def\@firstofthree#1#2#3{#1}% + \def\@secondofthree#1#2#3{#2}% + \newcommand{\DeclareGraphicsAlien}[3]{% + \edef\Gin@extensions{\Gin@extensions,#1}% + \DeclareGraphicsRule{#1}{\@gobble#1}{#1}{}% + \csdef{Gread@\@gobble#1}##1{% + \edef\SourceFile{\Gin@base\Gin@ext}% + \edef\Gin@base{\Gin@base-\@gobble#1-converted-to}% + \edef\Gin@ext{#2}% + \edef\OutputFile{\Gin@base\Gin@ext}% + \edef\targetfmt{\expandafter\expandafter\expandafter + \@firstofthree\csname Gin@rule@\Gin@ext\endcsname\relax}% + \edef\targetext{\expandafter\expandafter\expandafter + \@secondofthree\csname Gin@rule@\Gin@ext\endcsname\relax}% + \IfFileExists{\OutputFile}{}{\ShellEscape{#3}}% + \csletcs{Ginclude@\@gobble#1}{Ginclude@\targetfmt}% + \csname Gread@\targetfmt\endcsname{\Gin@base\targetext}% + }% + }% +% Support for GIF files: passed as-is in XML but converted in PNG for PDF output. +% In case the GIF is animated, we pick up the first frame in this conversion. +% While the picture will be animated after Moodle import, it will not be in the PDF +% output, whatever the viewer is. + \DeclareGraphicsAlien{.gif}{.png}{\htmlize@imagemagick@convert\otherspace '\SourceFile[0]' \OutputFile}% + }{}% +}% +% \end{macrocode} % \subsubsection{Graphics key-handling} % Next, we get ready to handle keys like |height=4cm| or |width=3cm| or |ppi=72|. % \begin{macrocode} @@ -3110,25 +5413,58 @@ % \end{macrocode} % % \subsubsection{Graphics conversion to HTML} +% If the |tikz| option is loaded, we define the |embedaspict| command. +% Furthermore, |includegraphics| is packed into a TikZ node. +% This allows externalization with regular options for |includegraphics|. +% Otherwise, |includegraphics| is redefined with a limited set of options supported. +% +% Option SVG % \begin{macrocode} -\html@action@def\includegraphics{\moodle@includegraphics@int}% - % Note that \htmlize@do@actioncs will 'patch' this by putting - % '\xa\htmlize@proceedwiththerest\htmlize@afteraction@hook' - % at the end. We want those 3 tokens to occur instead after - % the graphics filename. -\def\moodle@includegraphics@int#1#2#3{\moodle@includegraphics@int@int}% - % This gobbles up those three spurious tokens, - % which we will re-insert after our work is done. -\newcommand\moodle@includegraphics@int@int[2][]{% +\ifmoodle@svg + \AtEndPreamble{% +% Declaring SVG to PDF conversion rule for includegraphics +% \edef\Gin@extensions{\Gin@extensions,.svg,.SVG}% +% \DeclareGraphicsRule{.svg}{pdf}{.pdf}{% +% `\SVGtoPDF\otherspace '#1' \noexpand\Gin@base-svg-converted-to.pdf}% +% \DeclareGraphicsRule{.SVG}{pdf}{.pdf}{% +% `\SVGtoPDF\otherspace '#1' \noexpand\Gin@base-SVG-converted-to.pdf}% + \DeclareGraphicsAlien{.svg}{.pdf}{% + \SVGtoPDF\otherspace '\SourceFile' -o '\OutputFile' 2>/dev/null}% + \DeclareGraphicsAlien{.SVG}{.pdf}{% + \SVGtoPDF\otherspace '\SourceFile' -o '\OutputFile' 2>/dev/null}% + }% +\fi + +\ifmoodle@tikz +\AfterEndPreamble{% + %\htmlize@record@expand{\embedaspict}% + \let\oldincludegraphics=\includegraphics + % patching includegraphics to trigger externalization + \renewcommand{\includegraphics}[2][]{% + %\message{moodle.sty: Processing \string\includegraphics[#1]{#2} for HTML^^J}% + \tikz{\node[inner sep=0pt]{\oldincludegraphics[#1]{#2}};}% + }% + % externalized images must be included with the regular command + \pgfkeys{/pgf/images/include external/.code={\oldincludegraphics{#1}}}% + \html@action@newcommand{includegraphics}[2][]{% + \message{moodle.sty: Processing \string\includegraphics[#1]{#2} ^^J} + \global\advance\numpicturesread by 1\relax + \edef\htmlize@imagetag{<IMG SRC="data:\TikzExportMIME;base64,\csname picbaselxiv@\the\numpicturesread\endcsname">}% + \xa\g@addto@macro\xa\htmlize@output\xa{\htmlize@imagetag}% + }% +}% +\else +\html@action@newcommand{includegraphics}[2][]{% \bgroup% The grouping is to localize the changes caused by \setkeys. \message{moodle.sty: Processing \string\includegraphics[#1]{#2} for HTML...^^J} \setkeys*{moodle@includegraphics}{#1}% % Height or width should be given in TeX dimensions like cm or pt or in, % and are converted to pixels for web use using the ppi key. - % TO DO: Can we modify \includegraphics to accept height or width in + % TODO: Can we modify \includegraphics to accept height or width in % pixels? - % TO DO: What about \includegraphics[scale=0.7] ? + % TODO: What about \includegraphics[scale=0.7] ? % Other keys: keepaspectratio=true|false, angle (rotation), clip & trim + % -> the package option 'tikz' offers a workaround for this \ifnum\moodle@graphics@height@pixels=0\relax \ifnum\moodle@graphics@width@pixels=0\relax % No size specified. Default to height of 200 pixels. @@ -3150,28 +5486,89 @@ \edef\moodle@graphics@htmlgeometry{width=\number\moodle@graphics@width@pixels\otherspace height=\number\moodle@graphics@height@pixels}% \fi \fi - %First, convert it to PNG - \edef\cmdline{\htmlize@imagemagick@convert\otherspace #2 -resize \moodle@graphics@geometry\otherspace #2.png}% - \message{moodle.sty: Converting '#2' to PNG...^^J}% - \expandafter\immediate\expandafter\write18\expandafter{\cmdline}% - %Next, convert the PNG to base64 encoding - \def\cmdline{\openssl\otherspace enc -base64 -in #2.png -out #2.enc}% - \message{moodle.sty: Converting '#2.png' to base64...^^J}% - \expandafter\immediate\expandafter\write18\expandafter{\cmdline}% + %Look for the file, even if no extension is provided + \moodle@media@find{#2}% + %\message{<<\moodle@media@base>>^^J} + %\message{<<\moodle@media@ext>>^^J} + %Try to identify corresponding MIME-type + \moodle@media@mime@identify{\moodle@media@ext}% + \ifx\moodle@media@mime@current\relax + % conversion needed + \edef\moodle@media@pdf{\detokenize{.pdf}}% + \edef\moodle@media@ext{\detokenize\xa{\moodle@media@ext}}% + \ifnum0\ifx\moodle@media@ext\moodle@media@pdf1\fi\ifmoodle@svg1\fi=11% PDF file and SVG option active + \def\moodle@media@ext@current{.svg}% + \edef\moodle@media@mime@current{\moodle@media@SVGmime}% + \edef\cmdline{\PDFtoSVG\otherspace "\moodle@media@base\moodle@media@ext" -o "\moodle@media@base\moodle@media@ext@current" 2>/dev/null}% + \message{moodle.sty: Converting '#2' to SVG...^^J}% + \xa\ShellEscape\xa{\cmdline}% + %Next, optimize inline + \ifwindows\else + \edef\cmdline{\optiSVG < "\moodle@media@base\moodle@media@ext@current" > + "\moodle@media@base.tmp.svg" && mv "\moodle@media@base.tmp.svg" + "\moodle@media@base\moodle@media@ext@current"}% + \message{moodle.sty: Optimizing '\moodle@media@base\moodle@media@ext@current'...^^J}% + \xa\ShellEscape\xa{\cmdline}% + \fi + \else% call ImageMagick + \def\moodle@media@ext@current{.png}% + \def\moodle@media@mime@current{image/png}% + \edef\cmdline{\htmlize@imagemagick@convert\otherspace "\moodle@media@base\moodle@media@ext" -resize \moodle@graphics@geometry\otherspace "\moodle@media@base\moodle@media@ext@current"}% + \message{moodle.sty: Converting '#2' to PNG...^^J}% + \xa\ShellEscape\xa{\cmdline}% + %Next, optimize inline + \edef\cmdline{\optipng\otherspace "\moodle@media@base\moodle@media@ext@current"}% + \message{moodle.sty: Optimizing '\moodle@media@base\moodle@media@ext@current'...^^J}% + \xa\ShellEscape\xa{\cmdline}% + \fi + \fi + %Next, convert the file to base64 encoding + \ConvertToBaseLXIV{\moodle@media@base}{\moodle@media@ext@current}% %Now, save that base64 encoding in a TeX macro \def\moodle@newpic@baselxiv{}% - \message{moodle.sty: Reading base64 file '#2.enc'...^^J}% - \openin\baseLXIVdatafile=#2.enc\relax + \message{moodle.sty: Reading base64 file '\moodle@media@base.enc'...^^J}% + \openin\baseLXIVdatafile="\moodle@media@base.enc" \savebaselxivdata@recursive \closein\baseLXIVdatafile + %Clean up files + \ifx\moodle@media@ext@current\moodle@media@ext + \ShellEscape{\DeleteFiles\otherspace "\moodle@media@base.enc"}% + \else + \ShellEscape{\DeleteFiles\otherspace "\moodle@media@base.enc" "\moodle@media@base\moodle@media@ext@current"}% + \fi \xa\global\xa\let\csname picbaselxiv@graphics@#2\endcsname=\moodle@newpic@baselxiv% - \edef\htmlize@imagetag{<IMG \moodle@graphics@htmlgeometry\otherspace SRC="data:image/png;base64,\csname picbaselxiv@graphics@#2\endcsname">}% + \edef\htmlize@imagetag{<IMG \moodle@graphics@htmlgeometry\otherspace SRC="data:\moodle@media@mime@current;base64,\csname picbaselxiv@graphics@#2\endcsname">}% \xa\g@addto@macro\xa\htmlize@output\xa{\htmlize@imagetag}% \message{moodle.sty: <IMG> tag inserted.^^J}% \egroup - % Now we re-insert the code to get the HTMLizing going again. - \xa\htmlize@proceedwiththerest\htmlize@afteraction@hook -} +}% +\fi +% \end{macrocode} +% This macro is in charge of throwing a system call to convert local files to base64. +% \begin{macrocode} +\ifwindows + \def\ConvertToBaseLXIV#1#2{% + \def\cmdline{\baselxiv\otherspace -encode "#1#2"\otherspace tmp.b64 && findstr /vbc:"---" tmp.b64 > "#1" && del tmp.b64"}% + \message{moodle.sty: Converting '#1#2' to base64...^^J}% + \xa\ShellEscape\xa{\cmdline}% + % Starting from Windows 7, CertUtil is included by default. There should be no windows XP still running + }% +\else + \ifmacosx + \def\ConvertToBaseLXIV#1#2{% + \def\cmdline{\baselxiv\otherspace -b 64 -i "#1#2"\otherspace -o "#1.enc"}% + \message{moodle.sty: Converting '#1#2' to base64...^^J}% + \xa\ShellEscape\xa{\cmdline}% + }% + \else % Linux, Cygwin + \def\ConvertToBaseLXIV#1#2{% + \def\cmdline{\baselxiv\otherspace "#1#2"\otherspace > "#1.enc"}% + \message{moodle.sty: Converting '#1#2' to base64...^^J}% + \xa\ShellEscape\xa{\cmdline}% + }% + % base64 is part of coreutils, add "-w 64" to get exactly the previous behavior % + \fi +\fi % \end{macrocode} % The following code accomplishes the reading of an |.enc| file into memory. % It is also used by the \TikZ\ code below. @@ -3201,32 +5598,57 @@ % are undefined. % Our solution is to wait until |\AtBeginDocument| and then test whether % \TikZ\ is loaded. If so, we make the appropriate definitions. +% \begin{macro}{TikZ} +% \changes{v0.7}{2020/07/14}{Support \emph{tikz}\ command} % \begin{macrocode} -\newif\ifmoodle@tikzloaded -\moodle@tikzloadedfalse \AtBeginDocument{ \ifx\tikzpicture\@undefined \moodle@tikzloadedfalse \else \moodle@tikzloadedtrue \fi + \ifmoodle@draftmode + \long\def\tikzifexternalizing#1#2{#2}% + \else \ifmoodle@tikzloaded - \usetikzlibrary{external} + \usetikzlibrary{external}% \tikzexternalize% \tikzset{external/force remake}% - - \ifpdf - \def\pdftopng{\edef\gscmdline{\gs\otherspace -dBATCH -dNOPAUSE -sDEVICE=pngalpha -sOutputFile=\tikzexternalrealjob-tikztemp-\the\numconvertedpictures.png -r200 \tikzexternalrealjob-tikztemp-\the\numconvertedpictures.pdf}% - \expandafter\immediate\expandafter\write18\expandafter{\gscmdline}}% + \def\moodle@basename{\tikzexternalrealjob-tikztemp-\the\numconvertedpictures}% + \ifmoodle@svg + \def\TikzExportExtension{.svg}% + \def\TikzExportMIME{image/svg+xml}% + \ifpdfoutput% tex engines defaulting to PDF output (pdflatex, xelatex, lualatex) + \def\ExportTikz{ \message{moodle.sty: Converting picture '\moodle@basename.pdf' to SVG...^^J}% + \edef\cmdline{\PDFtoSVG\otherspace "\moodle@basename.pdf" -o "\moodle@basename\TikzExportExtension" 2>/dev/null}% + \xa\ShellEscape\xa{\cmdline}}% + \else % latex + \def\ExportTikz{ \message{moodle.sty: Converting picture '\moodle@basename.ps' to SVG...^^J}% + \edef\cmdline{\PDFtoSVG\otherspace "\moodle@basename.ps" -o "\moodle@basename\TikzExportExtension" 2>/dev/null}% + \xa\ShellEscape\xa{\cmdline}}% + \fi + \def\OptimizeExport{ \message{moodle.sty: Optimizing '\moodle@basename.svg'...^^J}% + \edef\cmdline{\optiSVG\otherspace < "\moodle@basename\TikzExportExtension" > + "\moodle@basename.tmp.svg" && mv "\moodle@basename.tmp.svg" + "\moodle@basename\TikzExportExtension" }% + \xa\ShellEscape\xa{\cmdline}}% \else - \def\pstopng{\edef\gscmdline{\gs\otherspace -dBATCH -dNOPAUSE -sDEVICE=pngalpha -sOutputFile=\tikzexternalrealjob-tikztemp-\the\numconvertedpictures.png -r200 \tikzexternalrealjob-tikztemp-\the\numconvertedpictures.ps}% - \expandafter\immediate\expandafter\write18\expandafter{\gscmdline}}% + \def\TikzExportExtension{.png}% + \def\TikzExportMIME{image/png}% + \ifpdfoutput% tex engines defaulting to PDF output (pdflatex, xelatex, lualatex) + \def\ExportTikz{ \message{moodle.sty: Converting picture '\moodle@basename.pdf' to PNG...^^J}% + \edef\gscmdline{\gs\otherspace -sOutputFile=\moodle@basename\TikzExportExtension\otherspace -r150 \moodle@basename.pdf}% + \xa\ShellEscape\xa{\gscmdline}}% + \else % latex + \def\ExportTikz{ \message{moodle.sty: Converting picture '\moodle@basename.ps' to PNG...^^J}% + \edef\gscmdline{\gs\otherspace -sOutputFile=\moodle@basename\TikzExportExtension\otherspace -r150 \\moodle@basename.ps}% + \xa\ShellEscape\xa{\gscmdline}}% + \fi + \def\OptimizeExport{ \message{moodle.sty: Optimizing '\moodle@basename.png'...^^J}% + \edef\cmdline{\optipng\otherspace \moodle@basename\TikzExportExtension}% + \xa\ShellEscape\xa{\cmdline}}% \fi - \def\pngtobaselxiv{\edef\opensslcmdline{\openssl\otherspace enc -base64 -in \tikzexternalrealjob-tikztemp-\the\numconvertedpictures.png -out \tikzexternalrealjob-tikztemp-\the\numconvertedpictures.enc}% - \expandafter\immediate\expandafter\write18\expandafter{\opensslcmdline}}% - \let\moodle@oldtikzpicture=\tikzpicture - %The following code lets us run things *before* the normal \begin{tikzpicture}. \renewenvironment{tikzpicture}{% \global\advance\numconvertedpictures by 1\relax @@ -3238,43 +5660,63 @@ % However, the tikz externalize library does *not* run \end{tikzpicture}. % In order to run commands after the tikz picture is done compiling, we need to % use a hook into \tikzexternal@closeenvironments. - \g@addto@macro{\tikzexternal@closeenvironments}{% \moodle@endtikzpicture@hook } +% The following could replace calls to \pdftopng, \pngoptim and \pngtobaselxiv +% \tikzset{external/system call/.add={}{; +% gs -dBATCH -dNOPAUSE -sDEVICE=pngalpha -sOutputFile="\image.png" -r150 "\image.pdf"; +% optipng -clobber -strip all -quiet "\image.png"; +% base64 "\image.png" > "\image.enc" +% } +% } +% With the following mechanism, we could trigger something when the externalized images are included back. +% \pgfkeys{/pgf/images/include external/.code={\pgfimage{#1}\@moodle@ifgeneratexml{\savebaselxivdata}{}}} +% \def\moodle@endtikzpicture@hook{% \@moodle@ifgeneratexml{% - \ifpdf - \message{moodle.sty: Converting picture '\tikzexternalrealjob-tikztemp-\the\numconvertedpictures.pdf' to PNG...^^J}% - \pdftopng - \else - \pstopng - \fi - \message{moodle.sty: Converting '\tikzexternalrealjob-tikztemp-\the\numconvertedpictures.png' to base64...^^J}% - \pngtobaselxiv + \ExportTikz + \OptimizeExport + \ConvertToBaseLXIV{\moodle@basename}{\TikzExportExtension}% \message{moodle.sty: Reading base64 file '\tikzexternalrealjob-tikztemp-\the\numconvertedpictures.enc'...^^J}% \savebaselxivdata \message{moodle.sty: base64 data saved.^^J}% }{}% } - \tikzset{external/optimize=true} - \tikzset{external/optimize command away={\VerbatimInput}{1}} + \ifmoodle@tikz + \tikzset{external/optimize=false}% due to redefinition, includegraphics must not be optimized away + \else + \tikzset{external/optimize=true}% + \tikzset{external/optimize command away={\VerbatimInput}{1}}% + \fi % % The HTMLizer version of the tikzpicture environment, % which writes an <IMG> tag to the XML file. - \htmlize@record@environment{tikzpicture} + \htmlize@record@environment{tikzpicture}% \g@addto@macro\htmlize@activate@environments{% \let\tikzpicture\relax\let\endtikzpicture\relax \NewEnviron{tikzpicture}[1][]{% \global\advance\numpicturesread by 1\relax - \edef\htmlize@imagetag{<IMG SRC="data:image/png;base64,\csname picbaselxiv@\the\numpicturesread\endcsname">}% + \edef\htmlize@imagetag{<IMG SRC="data:\TikzExportMIME;base64,\csname picbaselxiv@\the\numpicturesread\endcsname">}% \xa\g@addto@macro\xa\htmlize@output\xa{\htmlize@imagetag}% }[\htmlize@patchendenvironment]% }% + \html@action@newcommand{tikz}[2][]{% +% \message{>>> Processing \string\tikz[#1]{...} ^^J} + \global\advance\numpicturesread by 1\relax + \edef\htmlize@imagetag{<IMG SRC="data:\TikzExportMIME;base64,\csname picbaselxiv@\the\numpicturesread\endcsname">}% + \xa\g@addto@macro\xa\htmlize@output\xa{\htmlize@imagetag}% + }% \else %TikZ not loaded. Provide dummy definitions for commands. \long\def\tikzifexternalizing#1#2{#2}% \fi + \fi + \ifmoodle@tikz + \tikzstyle{moodlepict}=[minimum height=1em,inner sep=0pt,execute at begin node={\begin{varwidth}{\linewidth}},execute at end node={\end{varwidth}}] + \newcommand\embedaspict[1]{\tikz[baseline=-\the\dimexpr\fontdimen22\textfont2\relax]{\node[moodlepict]{\mbox{#1}};}} + \htmlize@record@expand{\embedaspict} + \fi } \newcount\numconvertedpictures @@ -3290,27 +5732,464 @@ \xa\global\xa\let\csname picbaselxiv@\the\numconvertedpictures\endcsname=\moodle@newpic@baselxiv% } -\def\@moodle@par{\par}% +\ifmoodle@tikz + \ifmoodle@tikzloaded + \PackageWarning{moodle}{With package option 'tikz', you should not load TikZ manually.}% + \fi + \RequirePackage{tikz}% + \RequirePackage{varwidth}% for the command |embedaspict| +\fi % \end{macrocode} % Finally, we clean up our mess by deleting the temporary PDF, PNG, and ENC % files we created. +% With XeTeX, we cannot clean pictures pdf's because they are actually included in the final pdf afterwards, by xdvipdfmx. +% With LuaTeX, it looks like |\AtEndDocument| is too early to for this cleaning step. % \begin{macrocode} -\AtEndDocument{% +\AfterEndDocument{% \ifmoodle@tikzloaded \@moodle@ifgeneratexml{% - \ifwindows - \immediate\write18{del \tikzexternalrealjob-tikztemp-*.*}% + \ifXeTeX + % we must keep picture pdf's for subsequent linking (xdvipdfmx) + \ifwindows + \ShellEscape{powershell.exe -noexit "del * -include \tikzexternalrealjob-tikztemp-*.* -exclude *.pdf}% + \else + \ShellEscape{find . -type f -name "\tikzexternalrealjob-tikztemp-*.*" -not -name "*.pdf" -delete}% + \fi \else - \immediate\write18{rm \tikzexternalrealjob-tikztemp-*.*}% + \ShellEscape{\DeleteFiles\otherspace \tikzexternalrealjob-tikztemp-*.*}% \fi }{}% \fi } - -% TO DO: +% \end{macrocode} +% TODO: % * sizing options for TikZ pictures? -% * In cloze multi, how to handle HTML or TeX in answers? In particular, what about ~? +%\end{macro} +% +% \subsection{Other Media} +% Support for other media comes through the |\url|\marg{link} and |\href|\marg{link}\marg{text} +% commands. +\AtEndPreamble{% + \@ifpackageloaded{hyperref}{% + \let\oldhref\href + \let\oldurl\url + \def\moodle@hyper@readexternallink#1#2#3#4:#5:#6\\#7{% +% 1) The link type (the string "link" in the cases I observed) +% 2) The URL fragment (i.e. what comes after # in the end), +% 3) the text replacement, +% 4) the URL scheme (http, https, mailto, file, run etc), +% 5) the URL details (in general, that is whole authority + path + query). +% If the URL contains a column (specification of a password in userinfo or a +% port in authority), we get here whatever comes before the first column (:) +% 6) empty is URL contains no column. Otherwise, filled with whatever follows +% a first column (:) after the scheme, +% 7) the whole URL again (fragment removed). + \def\filename@ext{}% + \def\filename@area{}% + \def\filename@base{}% + \ifx\\#6\\% if ##6 is empty (the URL contains no column -> no scheme -> local) + %\@hyper@linkfile file:#7\\{#3}{#2}{#7}% local file + %Local file (##1|##2|##3|##4|##5|##6): ##7\par + \filename@parse{#4}% + \ConvertToBaseLXIV{\filename@area\filename@base}{.\filename@ext}% + \else + \ifx\\#4\\%if ##4 is empty (no scheme was specified -> local file) +% %\@hyper@linkfile file:#7\\{#3}{#2}{#7}% Mac filename ? +% Local file (Mac? ##1|##2|##3|##4|##5|##6): ##7\par + \filename@parse{#4}% + \ConvertToBaseLXIV{\filename@area\filename@base}{.\filename@ext}% + \else + \def\@pdftempa{#4}% + \ifx\@pdftempa\@pdftempwordfile% scheme is "file:" +% %\@hyper@linkfile#7\\{#3}{#2}{#7}% file +% Local file (no column ##1|##2|##3|##4|##5|##6): ##7\par + \filename@parse{#5}% + \ConvertToBaseLXIV{\filename@area\filename@base}{.\filename@ext}% + \else + \ifx\@pdftempa\@pdftempwordrun% scheme is "run:" + \ifHy@pdfa + \Hy@Error{% + PDF/A: Launch action is prohibited% + }\@ehc + \begingroup + \leavevmode +% ##2% + \endgroup + \else% not in PDF/A mode -> run is allowed + %\@hyper@launch#7\\{#3}{#2}% run local file +% Run local file (##1|##2|##3|##4|##5|##6): ##7\par + \filename@parse{#5}% + \ConvertToBaseLXIV{\filename@area\filename@base}{.\filename@ext}% + \fi + \else% scheme is neither "file" nor "run", assuming it is a web protocol + %\hyper@linkurl{#3}{#7\ifx\\#2\\\else\hyper@hash#2\fi}% URL +% URL (##1|##2|##3|##4|##5|##6): ##7\par + \filename@parse{#7}% + \fi + \fi + \fi + \fi + \xdef\moodle@media@ext{.\filename@ext}% + \xdef\moodle@media@base{\filename@area\filename@base}% + }% +% \html@action@newcommand{href}[3][]{% +% \bgroup% The grouping is to localize the changes caused by \setkeys. +% \message{moodle.sty: Processing \string\href[#1]{#2}{#3} for HTML...^^J}% +% the following macro is a modified version of hyperref's |\hyper@readexternallink| +% \let\@hyper@readexternallink\moodle@hyper@readexternallink +% \oldhref[#1]{#2}{#3}% + %\message{<<\moodle@media@base>>^^J} + %\message{<<\moodle@media@ext>>^^J} + %Try to identify corresponding MIME-type +% \moodle@media@mime@identify{\moodle@media@ext}% + %\edef\moodle@media@mime@current{\detokenize\xa{\moodle@media@mime@current}}% +% \xa\message\xa{moodle.sty: \moodle@media@mime@current^^J}% +% \ifx\moodle@media@mime@current\relax +% \xa\g@addto@macro\xa\htmlize@output\xa{<A href=\otherampersand\otherhash34;\moodle@media@base\moodle@media@ext\otherampersand\otherhash34;>#3</A>}% +% \message{moodle.sty: <A> tag inserted.^^J}% +% \else +% \filename@parse{\moodle@media@mime@current}% +% \def\@tmp{audio/} +% \ifx\filename@area\@tmp +% \IfFileExists{\moodle@media@base.enc}{% +% \def\moodle@newpic@baselxiv{}% +% \openin\baseLXIVdatafile=\moodle@media@base.enc\relax +% \savebaselxivdata@recursive +% \closein\baseLXIVdatafile +% \xa\g@addto@macro\xa\htmlize@output\xa{<audio controls src="data:\moodle@media@mime@current;base64,\moodle@newpic@baselxiv">#3</audio>}% +% }{% +% \xa\g@addto@macro\xa\htmlize@output\xa{<audio controls src="\moodle@media@base\moodle@media@ext">#3</audio>}% +% }% +% \message{moodle.sty: <audio> tag inserted.^^J}% +% \else +% \def\@tmp{video/} +% \ifx\filename@area\@tmp +% \IfFileExists{\moodle@media@base.enc}{% +% \def\moodle@newpic@baselxiv{}% +% \openin\baseLXIVdatafile=\moodle@media@base.enc\relax +% \savebaselxivdata@recursive +% \closein\baseLXIVdatafile +% \xa\g@addto@macro\xa\htmlize@output\xa{<video controls src="data:\moodle@media@mime@current;base64,\moodle@newpic@baselxiv">#3</video>}% +% }{% +% \xa\g@addto@macro\xa\htmlize@output\xa{<video controls src="\moodle@media@base\moodle@media@ext">#3</video>}% +% }% +% \message{moodle.sty: <video> tag inserted.^^J}% +% \else +% \PackageWarning{moodle}{cannot recognize MIME type of #2. Ignoring it.}% +% \fi +% \fi +% \fi +% \egroup +% }% +% \html@action@def\url#1{% +% \bgroup% The grouping is to localize the changes caused by \setkeys. +% \message{moodle.sty: Processing \string\url{#1} for HTML...^^J}% +% the following macro is a modified version of hyperref's |\hyper@readexternallink| +% \let\@hyper@readexternallink\moodle@hyper@readexternallink +% \oldhref{#1}{#1}% +% \xa\g@addto@macro\xa\htmlize@output\xa{<A href=\otherampersand\otherhash34;\moodle@media@base\moodle@media@ext\otherampersand\otherhash34;>#1</A>}% +% \message{moodle.sty: <A> tag inserted.^^J}% +% \egroup +% }% + }{}% +}% +% +% \subsection{Verbatim Code} +% +% We start by defining a macro to parameter a style for code box display in Moodle +% \begin{macrocode} +\def\xmlDisplayVerbatimBox{border-top: thin solid; border-bottom: thin solid}% +% \end{macrocode} +% First, let us handle |\verbatiminput| from the `verbatim' package +% \begin{macrocode} +\html@action@def\verbatiminput#1{% + \message{moodle.sty: Processing \string\verbatiminput{#1} for HTML ^^J}% + \g@addto@macro\htmlize@output{<PRE style="\xmlDisplayVerbatimBox"><CODE>}% + %%%%%%%%%%%%%% from verbatim %%%%%%%%%%%%%%%%% + \@bsphack + \let\do\@makeother\dospecials + \catcode`\^^M\active + \def\verbatim@processline{\xa\g@addto@macro\xa\htmlize@output\xa{\the\verbatim@line<BR/>}} + \verbatim@readfile{#1}% + \@esphack + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \g@addto@macro\htmlize@output{</CODE></PRE>}% +}% +% \end{macrocode} +% Second, we deal with |\VerbatimInput| from `fancyvrb' or `fvextra' +% \begin{macrocode} +\@ifpackageloaded{minted}{\PackageError{moodle}{'moodle' should be loaded before 'minted'.}% +{'moodle' loads 'fancybox' which, unfortunately, redefines verbatim commands.}}{\relax}% +\@ifpackageloaded{fvextra}{\PackageError{moodle}{'moodle' should be loaded before 'fvextra'.}% +{'moodle' loads 'fancybox' which, unfortunately, redefines verbatim commands.}}{\relax}% +\@ifpackageloaded{fancyvrb}{\PackageError{moodle}{'moodle' should be loaded before 'fancyvrb'.}% +{'moodle' loads 'fancybox' which, unfortunately, redefines verbatim commands.}}{\relax}% + +\html@action@newcommand{VerbatimInput}[2][]{% + \message{moodle.sty: Processing \string\VerbatimInput[#1]{#2} for HTML ^^J}% + \def\FV@KeyValues{#1}% + \FV@UseKeyValues% import options defined in #1 + \moodle@VerbatimInput{#2}% +}% +\html@action@newcommand{LVerbatimInput}[2][]{% + \message{moodle.sty: Processing \string\LVerbatimInput[#1]{#2} for HTML ^^J}% + \def\FV@KeyValues{#1}% + \FV@UseKeyValues% import options defined in #1 + \moodle@VerbatimInput{#2}% +}% +\html@action@newcommand{BVerbatimInput}[2][]{% + \message{moodle.sty: Processing \string\BVerbatimInput[#1]{#2} for HTML ^^J}% + \def\FV@KeyValues{#1}% + \FV@UseKeyValues% import options defined in #1 + \moodle@VerbatimInput{#2}% +}% +\def\moodle@VerbatimInput#1{% + \g@addto@macro\htmlize@output{<PRE style="\xmlDisplayVerbatimBox"><CODE>}% + %%%%%%%% using material from fancyvrb and fvextra %%%%%%%% + %\begingroup + \def\moodle@verbatim@addlinenumber##1{% + \g@addto@macro\htmlize@output{<span style="font-size: 80\otherpercent; + background-color: \otherhash f0f0f0; padding: 0 5px 0 5px; display: + inline-block; width: 16pt; ##1">}% + \if@FV@NumberBlankLines + \xa\g@addto@macro\xa\htmlize@output\xa{\the\c@FancyVerbLine</span>}% + \else + \ifx\FV@Line\empty + \xa\g@addto@macro\xa\htmlize@output\xa{\otherampersand nbsp;</span>}% + \else + \xa\g@addto@macro\xa\htmlize@output\xa{\the\c@FancyVerbLine</span>}% + \fi + \fi + } + % redefine the ProcessLine routine ('fancyvrb' and 'fvextra') for XML output + \def\FV@ProcessLine##1{% + \ifcsname FV@HighlightLine:\number\c@FancyVerbLine\endcsname + \xdef\moodle@FV@tagB{<mark>}% fvextra triggered highlighting for this line + \xdef\moodle@FV@tagE{</mark>}% + \else + \xdef\moodle@FV@tagB{}% no highlighting + \xdef\moodle@FV@tagE{}% + \fi + \catcode`\`=12% + \def\FV@Line{##1}% + \ifx\FV@LeftListNumber\relax + + \else% line numbers displayed on the left side + \moodle@verbatim@addlinenumber{text-align: right}% + \fi + \xa\g@addto@macro\xa\htmlize@output\xa{\moodle@FV@tagB}% + \xa\g@addto@macro\xa\htmlize@output\xa{\FV@Line}% + \xa\g@addto@macro\xa\htmlize@output\xa{\moodle@FV@tagE}% + \ifx\FV@RightListNumber\relax\else% line numbers on the right side + \moodle@verbatim@addlinenumber{text-align: left; float: right}% + \fi + \g@addto@macro\htmlize@output{<BR/>}%linebreak + } + \global\FV@CodeLineNo\z@% reset codeline counter + \frenchspacing% Cancels special punctuation spacing. + \FV@DefineWhiteSpace + \def\FV@Space{\space} + \FV@DefineTabOut% replace tabs with a series a whitespaces + \ifdefined\FV@HighlightLinesPrep + \FV@HighlightLinesPrep% prepare highlighting if 'fvextra' is loaded + \fi + \FV@Input{#1}% + %\endgroup + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \g@addto@macro\htmlize@output{</CODE></PRE>}% +}% +\AtEndPreamble{% + \@ifpackageloaded{fancyvrb}{% + % custom settings for display + \fvset{frame=lines,label={[Beginning of code]End of code},framesep=3mm,numbersep=9pt}% + }{\relax}% +} +% \end{macrocode} +% Third, we patch `minted' so that it also calls pygmentize to generate HTML code. +% \begin{macrocode} +\AtEndPreamble{% this definition should prevail because `minted' gets loaded after `moodle' +\@ifpackageloaded{minted}{% + \newcounter{moodle@pygmentizecounter}% + \renewcommand{\minted@pygmentize}[2][\minted@outputdir\minted@jobname.pyg]{% + \minted@checkstyle{\minted@get@opt{style}{default}}% + \stepcounter{minted@pygmentizecounter}% + \ifthenelse{\equal{\minted@get@opt{autogobble}{false}}{true}}% + {\def\minted@codefile{\minted@outputdir\minted@jobname.pyg}}% + {\def\minted@codefile{#1}}% + \ifthenelse{\boolean{minted@isinline}}% + {\def\minted@optlistcl@inlines{% + \minted@optlistcl@g@i + \csname minted@optlistcl@lang\minted@lang @i\endcsname}}% + {\let\minted@optlistcl@inlines\@empty}% + \def\minted@cmdtemplate##1##2{% + \ifminted@kpsewhich + \ifwindows + \detokenize{for /f "usebackq tokens=*"}\space\@percentchar\detokenize{a + in (`kpsewhich}\space\minted@codefile\detokenize{`) do}\space + \fi + \fi + \MintedPygmentize\space -l #2 -f ##1 -F tokenmerge + \minted@optlistcl@g \csname minted@optlistcl@lang\minted@lang\endcsname + \minted@optlistcl@inlines + \minted@optlistcl@cmd -o \minted@outputdir##2\space + \ifminted@kpsewhich + \ifwindows + \@percentchar\detokenize{a}% + \else + \detokenize{`}kpsewhich \minted@codefile\space + \detokenize{||} \minted@codefile\detokenize{`}% + \fi + \else + \minted@codefile + \fi}% + \def\minted@cmd{\minted@cmdtemplate{latex -P commandprefix=PYG}{\minted@infile}} + % For debugging, uncomment: %%%% + \immediate\typeout{\minted@cmd}% + % %%%% + \def\minted@cmdHTML{\minted@cmdtemplate{html -P noclasses -P + nowrap -P hl_lines="\FV@HighlightLinesList" -P + style="\minted@get@opt{style}{default}"}{\csname minted@infileHTML\the\c@minted@pygmentizecounter\endcsname}}% + \def\minted@cmdPNG{\minted@cmdtemplate{png -P + line_numbers=True}{\minted@infilePNG}}% + \def\minted@cmdSVG{\minted@cmdtemplate{svg -P + linenos=True}{\minted@infileSVG}}% + \ifthenelse{\boolean{minted@cache}}% + {% + \ifminted@frozencache + \else + \ifx\XeTeXinterchartoks\minted@undefined + \ifthenelse{\equal{\minted@get@opt{autogobble}{false}}{true}}% + {\edef\minted@hash{\pdf@filemdfivesum{#1}% + \pdf@mdfivesum{\minted@cmd autogobble(\ifx\FancyVerbStartNum\z@ + 0\else\FancyVerbStartNum\fi-\ifx\FancyVerbStopNum\z@ + 0\else\FancyVerbStopNum\fi)}}}% + {\edef\minted@hash{\pdf@filemdfivesum{#1}% + \pdf@mdfivesum{\minted@cmd}}}% + \else + \ifx\mdfivesum\minted@undefined + \immediate\openout\minted@code\minted@jobname.mintedcmd\relax + \immediate\write\minted@code{\minted@cmd}% + \ifthenelse{\equal{\minted@get@opt{autogobble}{false}}{true}}% + {\immediate\write\minted@code{autogobble(\ifx\FancyVerbStartNum\z@ + 0\else\FancyVerbStartNum\fi-\ifx\FancyVerbStopNum\z@ + 0\else\FancyVerbStopNum\fi)}}{}% + \immediate\closeout\minted@code + \edef\minted@argone@esc{#1}% + \StrSubstitute{\minted@argone@esc}{\@backslashchar}{\@backslashchar\@backslashchar}[\minted@argone@esc]% + \StrSubstitute{\minted@argone@esc}{"}{\@backslashchar"}[\minted@argone@esc]% + \edef\minted@tmpfname@esc{\minted@outputdir\minted@jobname}% + \StrSubstitute{\minted@tmpfname@esc}{\@backslashchar}{\@backslashchar\@backslashchar}[\minted@tmpfname@esc]% + \StrSubstitute{\minted@tmpfname@esc}{"}{\@backslashchar"}[\minted@tmpfname@esc]% + %Cheating a little here by using ASCII codes to write `{` and `}` + %in the Python code + \def\minted@hashcmd{% + \detokenize{python -c "import hashlib; import os; + hasher = hashlib.sha1(); + f = + open(os.path.expanduser(os.path.expandvars(\"}\minted@tmpfname@esc.mintedcmd\detokenize{\")), + \"rb\"); + hasher.update(f.read()); + f.close(); + f = + open(os.path.expanduser(os.path.expandvars(\"}\minted@argone@esc\detokenize{\")), + \"rb\"); + hasher.update(f.read()); + f.close(); + f = + open(os.path.expanduser(os.path.expandvars(\"}\minted@tmpfname@esc.mintedmd5\detokenize{\")), + \"w\"); + macro = \"\\edef\\minted@hash\" + chr(123) + hasher.hexdigest() + + chr(125) + \"\"; + f.write(\"\\makeatletter\" + macro + + \"\\makeatother\\endinput\n\"); + f.close();"}}% + \ShellEscape{\minted@hashcmd}% + \minted@input{\minted@outputdir\minted@jobname.mintedmd5}% + \else + \ifthenelse{\equal{\minted@get@opt{autogobble}{false}}{true}}% + {\edef\minted@hash{\mdfivesum file {#1}% + \mdfivesum{\minted@cmd autogobble(\ifx\FancyVerbStartNum\z@ + 0\else\FancyVerbStartNum\fi-\ifx\FancyVerbStopNum\z@ + 0\else\FancyVerbStopNum\fi)}}}% + {\edef\minted@hash{\mdfivesum file {#1}% + \mdfivesum{\minted@cmd}}}% + \fi + \fi + \edef\minted@infile{\minted@cachedir/\minted@hash.pygtex}% + \edef\minted@temp@infileHTML{\minted@cachedir/\minted@hash.html}% + \global\cslet{minted@infileHTML\the\c@minted@pygmentizecounter}{\minted@temp@infileHTML}% + %\global\edef\minted@infilePNG{\minted@cachedir/\minted@hash.png}% + %\global\edef\minted@infileSVG{\minted@cachedir/\minted@hash.svg}% + \IfFileExists{\minted@infile}{}{% + \ifthenelse{\equal{\minted@get@opt{autogobble}{false}}{true}}{% + \minted@autogobble{#1}}{}% + \ShellEscape{\minted@cmd}% + \ShellEscape{\minted@cmdHTML}% + %\ShellEscape{\minted@cmdPNG}% + %\ShellEscape{\minted@cmdSVG}% + }% + \fi + \ifthenelse{\boolean{minted@finalizecache}}% + {% + \edef\minted@cachefilename{listing\arabic{minted@pygmentizecounter}.pygtex}% + \edef\minted@actualinfile{\minted@cachedir/\minted@cachefilename}% + \ifwindows + \StrSubstitute{\minted@infile}{/}{\@backslashchar}[\minted@infile@windows] + \StrSubstitute{\minted@actualinfile}{/}{\@backslashchar}[\minted@actualinfile@windows] + \ShellEscape{move /y + \minted@outputdir\minted@infile@windows\space\minted@outputdir\minted@actualinfile@windows}% + \else + \ShellEscape{mv -f + \minted@outputdir\minted@infile\space\minted@outputdir\minted@actualinfile}% + \fi + \let\minted@infile\minted@actualinfile + \expandafter\minted@addcachefile\expandafter{\minted@cachefilename}% + }% + {\ifthenelse{\boolean{minted@frozencache}}% + {% + \edef\minted@cachefilename{listing\arabic{minted@pygmentizecounter}.pygtex}% + \edef\minted@infile{\minted@cachedir/\minted@cachefilename}% + \expandafter\minted@addcachefile\expandafter{\minted@cachefilename}}% + {\expandafter\minted@addcachefile\expandafter{\minted@hash.pygtex}}% + }% + \minted@inputpyg}% + {% + \ifthenelse{\equal{\minted@get@opt{autogobble}{false}}{true}}{% + \minted@autogobble{#1}}{}% + \ShellEscape{\minted@cmd}% + \ShellEscape{\minted@cmdHTML}% + %\ShellEscape{\minted@cmdPNG}% + %\ShellEscape{\minted@cmdSVG}% + \minted@inputpyg}% + }% +}{}% +}% +\html@action@newcommand{inputminted}[3][]{% + \message{moodle.sty: Processing \string\inputminted[#1]{#2}{#3} for HTML ^^J}% + % arguments #2 and #3 are thrown away: the job is done previously by minted when + % calling pygmentize. The file |\minted@infileHTML| generated with our hack will be used. + % Since minted is based upon `fvextra' the macro |\moodle@VerbatimInput| works here. + \minted@configlang{#2}% grab options set for this specific language + \setkeys{minted@opt@cmd}{#1}% grab options in #1 + \minted@fvset% import options + \stepcounter{moodle@pygmentizecounter} + \xa\moodle@VerbatimInput\xa{\csname minted@infileHTML\the\c@moodle@pygmentizecounter\endcsname}% +}% % \end{macrocode} % +% \subsection{Warning users of \texttt{babel}} +% Users of the |babel| package loaded with the 'french' option may experience problems +% related to autospacing when using |pdfLaTeX|. We shall warn them. +% \begin{macrocode} +\AtBeginDocument{% + \ifPDFTeX % pdflatex or latex + \@ifpackagewith{babel}{french}{% + \PackageWarning{moodle}{Be careful when using LaTeX, moodle, and the babel package with option 'french'. Autospacing produces in undesired symbols in the XML. You can either 1) compile with xelatex/lualatex or 2) add '\NoAutoSpacing' after '\begin{quiz}'.}% + }{\relax}% + \fi +}% +% \end{macrocode} +% +% % \Finale \endinput diff --git a/Master/texmf-dist/source/latex/moodle/moodle.ins b/Master/texmf-dist/source/latex/moodle/moodle.ins index 16afd4503b5..b118c1beabc 100644 --- a/Master/texmf-dist/source/latex/moodle/moodle.ins +++ b/Master/texmf-dist/source/latex/moodle/moodle.ins @@ -1,5 +1,6 @@ %% moodle.ins -%% Copyright 2016 by Anders O.F. Hendrickson +%% Copyright 2016 by Anders O.F. Hendrickson and 2019-2021 +%% by Matthieu Guerquin-Kern. %% %% This work may be distributed and/or modified under the %% conditions of the LaTeX Project Public License, either version 1.3 @@ -11,7 +12,7 @@ %% %% This work has the LPPL maintenance status `maintained'. %% -%% The Current Maintainer of this work is Anders O.F. Hendrickson. +%% The Current Maintainer of this work is Matthieu Guerquin-Kern. %% %% This work consists of the files moodle.dtx and moodle.ins %% and the derived file moodle.sty. @@ -25,7 +26,8 @@ This is a generated file. -Copyright (C) 2016 by Anders Hendrickson <anders.hendrickson@snc.edu> +Copyright 2016 by Anders O.F. Hendrickson (anders.o.f.hendrickson AT gmail.com) +and 2019-2021 by Matthieu Guerquin-Kern (guerquin-kern AT crans.org). This work may be distributed and/or modified under the conditions of the LaTeX Project Public License, either version 1.3 |