% \iffalse meta-comment % % moodle.dtx % Copyright 2016 by Anders O.F. Hendrickson (anders.hendrickson@snc.edu) % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Anders O.F. Hendrickson. % % This work consists of the files moodle.dtx and moodle.ins % and the derived files moodle.sty and getitems.sty. % % \fi % % \iffalse %<*driver> \ProvidesFile{moodle.dtx} % %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\ProvidesPackage{moodle} %<*package> [2016/01/11 v0.5 Moodle quiz XML generation] % % %<*driver> \documentclass{ltxdoc} \usepackage{moodle} \usepackage{amssymb} \usepackage{fancyvrb} \EnableCrossrefs \CodelineIndex \OnlyDescription \RecordChanges \begin{document} \DocInput{moodle.dtx} \PrintChanges %\PrintIndex \end{document} % % \fi % % \CheckSum{3827} % % \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 % Lower-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 % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % % \changes{v0.5}{2016/01/05}{Initial version} % % \GetFileInfo{moodle.sty} % % \DoNotIndex{\newcommand,\newenvironment,\def} % % % \title{The \textsf{moodle} package: \\ % 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} % % \maketitle % % \providecommand\TikZ{Ti\emph{k}Z} % % \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 % notes, video lectures, forums, and the like; see % \url{https://moodle.com/moodle-lms/} for more information. % One of the many useful % features of Moodle is that mathematical and scientific notation can be entered in % \LaTeX\ or \TeX\ code, which will be typeset either through a built-in \TeX\ filter % or by invoking MathJax. % % For instructors who want to give students frequent feedback, % but lack the time to do so, a particularly valuable module in Moodle % is the \emph{quiz}. A Moodle quiz can consist of several different types of % questions---not only multiple choice or true/false questions, but also % questions requiring a short phrase or numerical answer, and even essay % questions. All but the essay questions are automatically graded by the % system, and the instructor has full control over how often the quiz may be % attempted, its duration, and so forth. Feedback can be tailored to specific % mistakes the student makes. % % All these features make Moodle quizzes very useful tools for instructors % who have access to them. % Unfortunately, the primary way to create or edit a Moodle quiz % is through a web-based interface that can be slow to operate. % To users of \LaTeX, accustomed to the speed of typing source code on % a keyboard alone, the agonizing slowness of switching between mouse and keyboard % to navigate a web form with its myriad dropdown boxes, radio buttons, % compounded with a perceptible time lag as one's Moodle server responds to requests, % can produce a very frustrating experience. Moreover, editing is entirely % impossible without network access. % % Once the quiz is written, there is no easy way to view and proofread all the % information of which it is made. Each question is edited on a separate webpage, % which is so full of options that it cannot be viewed on a single screen. % An instructor has to spend much time checking over the newly created quiz in % order to be confident there are no errors. % % Added to all this is the frustration of managing graphics. % If a question requires an image---say, asking a calculus student to interpret % the graph of a function---the image must first be produced as a standalone file % (e.g., in JPG or PNG format), uploaded to Moodle, and then chosen in a web-based % HTML editor. Great is the vexation of the instructor who decided to alter a % question, as there are more and more possibilities of error whenever multiple % files must be kept synchronized. % % Users of \LaTeX\ are also accustomed to the speed and flexibility that % comes from defining their own macros, which may be as brief as writing % |\R| instead of |\mathbb{R}| % or as complex as macros that generate entire paragraphs of text. % The Moodle editor, by contrast, requires you to type |\mathbb{R}| % every single time you want $\mathbb{R}$. % % Finally, there is the question of archiving and reusing one's work. % Much, much work goes into creating Moodle quizzes, which then reside % on a Moodle server somewhere in the cloud in a format neither easily browsable % nor easily modifiable. % % \LaTeX\ itself has the power to solve all these difficulties: % it is swift to edit and swifter to compile a \LaTeX\ document, % 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, % 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. % % \section{Workflow} % The process of creating a quiz in Moodle using this package is as follows: % \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 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} % % \section{Usage}\label{sect:usage} % \subsection{Example Document} % % The following pages presume the reader already has some familiarity with creating % 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} % \documentclass[12pt]{article} % \usepackage{moodle} % % \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} % What was Newton's first name? % \item Isaac % \item[fraction=0, feedback={No, silly!}] 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$ % \end{multi} % \end{quiz} % % \end{document} % \end{verbatim} % 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, % and it may also take optional key-value arguments within brackets. % The question environments resemble list environments % 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|. % % \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. % % \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. % \begin{center} % |\begin{quiz}|\oarg{options}\marg{question bank 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 % % 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}\quad$\vdots$ \\ % \rule{2em}{0pt}|\item| \meta{item} \\ % |\end|\marg{question type} % \end{quote} % The meaning of the \meta{item}s varies depending on the question type, % but they usually are answers to the question. % Details will be given below. % % The following key-value options may be set for all questions: % % \DescribeMacro{points}\DescribeMacro{default grade} % By default, each question is worth 1 point on the quiz. % This may be changed with the |points| key or its synonym, |default grade|; % for example, |points=2| makes that question worth two points. % % \DescribeMacro{penalty} % The |penalty| is the fraction of points that is taken off for each wrong attempt; % it may be set to any value between 0 and 1. % The default is |penalty=0.10|. % % \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). % % \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.}| % \end{center} % The desired feedback should be included in braces. % % Two kinds of feedback can be given. If the |feedback| key is set for a % 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}. % % \newpage % \subsection{Question Types} % % We next discuss the various question types supported by \textsf{moodle.sty} % and the options that may be set. % % \subsubsection{Multiple Choice} % % \DescribeMacro{multi} % The syntax for a classic multiple choice question, % with only one correct answer, is as follows: % \begin{quote} % |\begin{multi}|\oarg{question options}\marg{question name} \\ % \rule{2em}{0pt}\meta{question text} \\ % \rule{2em}{0pt}|\item*| \meta{correct answer} \\ % \rule{2em}{0pt}|\item|\oarg{options} \meta{wrong answer} \\ % \rule{2em}{0pt}\quad$\vdots$ \\ % \rule{2em}{0pt}|\item|\oarg{options} \meta{wrong answer} \\ % |\end{multi}| % \end{quote} % The correct answer is designated by the asterisk |*| after the |\item|; % it need not appear first in the list. % % % \DescribeMacro{shuffle} % The boolean key |shuffle| determines whether Moodle will % rearrange the possible answers in a random order. % Setting |shuffle=false| will guarantee that the answer appear % in the order they were typed; the default is |shuffle=true|. % % \DescribeMacro{numbering} % Moodle offers different options for numbering the possible answers. % You may set the |numbering| key to any of the following values, % 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. % % \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{multi}{my question} % Compute $\int 4x^3\,dx$. % \item* $x^4+C$ % \item[fraction=50] $x^4$ % \item $12x^2$ % \end{multi} % \end{verbatim} % Thus the asterisk |*| is shorthand for |fraction=100|, % whereas a bare |\item| sets |fraction=0|. % % \DescribeMacro{single}\DescribeMacro{multiple} % By default, the |multi| environment produces % a multiple choice question operates 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|. % % 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} % \begin{multi}[multiple]{my question} % Which numbers are prime? % \item* 5 % \item 6 % \item* 7 % \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 % \end{multi} % \end{verbatim} % % \subsubsection{Numerical} % % A numerical question in Moodle requires the student % to input a real number in decimal form. % Its typical format is % \begin{quote} % |\begin{numerical}|\oarg{question options}\marg{question name} \\ % \rule{2em}{0pt}\meta{question text} \\ % \rule{2em}{0pt}|\item|\oarg{options} \meta{correct answer} \\ % |\end{numerical}| % \end{quote} % If there is more than one correct answer, additional |\item|'s may be included. % Because this is not a multiple choice question, there is no need to provide % incorrect answers. There may nevertheless be reasons to include incorrect answers. % For example, partially correct answers may be specified by setting the |fraction| key. % Feedback for a common mistake may be given by including the incorrect answer like this: % \begin{quote}\footnotesize % |\item[fraction=0,feedback={You forgot to antidifferentiate!}]| \meta{incorrect answer} % \end{quote} % % \newpage % \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. % \begin{quote} % |\begin{shortanswer}|\oarg{question options}\marg{question name} \\ % \rule{2em}{0pt}\meta{question text} \\ % \rule{2em}{0pt}|\item|\oarg{options} \meta{correct answer} \\ % \rule{2em}{0pt}\quad$\vdots$ \\ % \rule{2em}{0pt}|\item|\oarg{options} \meta{correct answer} \\ % |\end{shortanswer}| % \end{quote} % 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{shortanswer}{Leibniz} % Newton's rival was Gottfried Wilhelm \blank. % \item Leibniz % \item Leibniz. % \end{shortanswer} % \end{verbatim} % 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. % You can make a question case-sensitive with the key |case sensitive| % or its shorter synonym |usecase|. % % \subsubsection{Essay Questions} % 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 % instructor or a teaching assistant. % \begin{quote} % |\begin{essay}|\oarg{question options}\marg{question name} \\ % \rule{2em}{0pt}\meta{question text} \\ % \rule{2em}{0pt}|\item|\oarg{options} \meta{notes for grader} \\ % \rule{2em}{0pt}\quad$\vdots$ \\ % \rule{2em}{0pt}|\item|\oarg{options} \meta{notes for grader} \\ % |\end{essay}| % \end{quote} % Instead of containing answers, the |\item| tags for the |essay| question % contain notes that will appear to whoever is grading the question manually. % % \DescribeMacro{response required} % Although Moodle cannot grade the content of an essay question, % it can at least determine whether the question has been left blank. % 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 % 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. % \end{description} % % \DescribeMacro{response field lines} % The key |response field lines| controls the height of the input box. % The default is |response field lines=15|. % % \DescribeMacro{attachments allowed} % The |attachments allowed| key controls \emph{how many} attachments a student is % allowed to upload. Permissible values are |0|, |1|, |2|, |3|, or |unlimited|. % % \DescribeMacro{attachments required} % 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} % 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}. % The \meta{template} should be enclosed in braces. % % \subsection{Matching Questions} % % 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, % a matching question will look like this: % % \begin{quote} % |\begin{matching}|\oarg{question options}\marg{question name} \\ % \rule{2em}{0pt}\meta{question text} \\ % \rule{2em}{0pt}|\item|\oarg{options} \meta{question 1} |\answer| \meta{answer 1}\\ % \rule{2em}{0pt}|\item|\oarg{options} \meta{question 2} |\answer| \meta{answer 2}\\ % \rule{2em}{0pt}\quad$\vdots$ \\ % \rule{2em}{0pt}|\item|\oarg{options} \meta{question $m$} |\answer| \meta{answer $m$}\\ % \rule{2em}{0pt}|\item|\oarg{options} |\answer| \meta{answer $m+1$}\\ % \rule{2em}{0pt}\quad$\vdots$ \\ % \rule{2em}{0pt}|\item|\oarg{options} |\answer| \meta{answer $n$}\\ % |\end{matching}| % \end{quote} % Answers $1$ through $m$ correspond to questions $1$ through $m$; % answers $m+1$ through $n$ are ``decoy'' answers. % If multiple questions should have the same answer, % be sure your typed answer match exactly, so that Moodle will not % create duplicate copies of the same answer! % % \DescribeMacro{shuffle} % The |matching| question accepts the option of |shuffle| to randomly % 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 % 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. % % \subsection{Cloze Questions} % % 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 % a sentence, or calculate several coefficients of a polynomial. % 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} \\ % Thanks to calculus, invented by Isaac % \begin{shortanswer} % \item Newton % \end{shortanswer}, % we know that the derivative of $x^2$ is % \begin{multi}[horizontal] % \item $2x$ % \item* $\frac{1}{3} x^3 + C$ % \item $0$ % \end{multi} % and that $\int_0^2 x^2\,dx$ equals % \begin{numerical} % \item[tolerance=0.0004] 2.667 % \end{numerical}. % Thanks, Isaac! % \end{cloze} % \end{verbatim} % 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. % 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. % % \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. % 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. % % 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 % 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. % % 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} % 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. % 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 % 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. % % \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|. % 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 % number was selected because an image with % || showed up as almost exactly 1 inch % tall and 1 inch wide on several of this author's devices and browsers % as of January 2016.} % \DescribeMacro{ppi} % 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. % \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| % (if |\ifwindows| from the |ifplatform| package returns true) % 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, % 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}. % \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 % 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}. % \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|. % \end{itemize} % % \newpage % \section{Known Limitations and Call for Bug Reports} % Some features of Moodle quizzes have not yet been implemented in \textsf{moodle.sty}. % 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. % \item Hints % \item Keywords % \item So-called ``description'' questions. % \item Shuffled answers in multiple choice cloze subquestions. % \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. % % \section{Compatibility} % This package has been 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. % % 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 % 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 % (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 % 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 % as low-resolution PNG's. One solution that has worked for me is to go % to ``Course Administration $\to$ Filters,'' turn ``\TeX\ Notation'' \emph{off}, % but turn ``MathJax'' \emph{on}. This forces \TeX\ code to be rendered by MathJax % instead of Moodle, producing much higher-quality results. % % \StopEventually{} % % \section{Implementation} % \subsection{Packages, Options, and Utilities} % \begin{macrocode} \newif\ifmoodle@draftmode \newif\ifmoodle@tikzloaded %%DECLARATION OF OPTIONS \DeclareOption{draft}{\moodle@draftmodetrue} \moodle@draftmodefalse \moodle@tikzloadedfalse \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{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 \RequirePackage{getitems} %To gather the header and items \let\xa=\expandafter \def\@star{*}% \def\@hundred{100}% \def\@moodle@empty{}% \def\@relax{\relax}% % \end{macrocode} % As the package involves a fair bit of file processing, % we automate the naming of auxiliary files. % \begin{macrocode} \def\jobnamewithsuffixtomacro#1#2{% \filenamewithsuffixtomacro{#1}{\jobname}{#2}% } \def\@jn@quote{"}% \def\filenamewithsuffixtomacro#1#2#3{% % #1 = macro to create % #2 = filename to add suffix to % #3 = suffix to add \edef\jn@suffix{#3}% \def\jn@macro{#1}% \xa\testforquote#2\@jn@rdelim } \def\testforquote#1#2\@jn@rdelim{% \def\jn@test@i{#1}% \ifx\jn@test@i\@jn@quote % Involves quotes \edef\jn@next{"\jn@stripquotes#1#2\jn@suffix"}% \else \edef\jn@next{#1#2\jn@suffix}% \fi \xa\xdef\jn@macro{\jn@next}% } \def\jn@stripquotes"#1"{#1}% \jobnamewithsuffixtomacro{\outputfilename}{-moodle.xml} % \end{macrocode} % Next, we create macros to open and close the Moodle XML file % we will be writing. % \begin{macrocode} \newwrite\moodle@outfile \def\openmoodleout{% \immediate\openout\moodle@outfile=\outputfilename\relax \writetomoodle{}% \writetomoodle{}% \writetomoodle{ }% }% \def\closemoodleout{% \writetomoodle{ }% \writetomoodle{}% \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 % |\moodle@indent| to be a string of \meta{$n$} |\otherspace|'s. % \begin{macrocode} \def\calculateindent#1{% \bgroup \count0=\number#1\relax \gdef\moodle@indent{}% \calculateindent@int \egroup }% \def\calculateindent@int{% \ifnum\count0>0\relax \g@addto@macro{\moodle@indent}{\otherspace}% \advance\count0 by -1\relax \expandafter \calculateindent@int \fi }% % \end{macrocode} % Now the command |\writetomoodle|\oarg{n}\marg{stuff} adds the line % ``\meta{stuff}'' to the XML file % preceded by an indent of \meta{n} spaces. % Note that this command |\edef|'s its second argument. % \begin{macrocode} \newcommand\writetomoodle[2][0]{% \edef\test@ii{#2}% \ifnum#1=0\relax \immediate\write\moodle@outfile{\test@ii}% \else \calculateindent{#1}% \immediate\write\moodle@outfile{\moodle@indent\trim@pre@space{\test@ii}}% \fi }% % \end{macrocode} % We now create the main |quiz| environment that will contain % the questions we write. It outputs to XML as a || 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{% \writetomoodle{}% \writetomoodle{ }% \writetomoodle{ $module$/#1}% \writetomoodle{ }% \writetomoodle{}% \writetomoodle{ }% }}% % \end{macrocode} % The next utility takes a single macro control sequence |#1|, % and allows that macro's current value to persist % after the next |\egroup|, |}|, or |\endgroup|. % \begin{macrocode} \def\passvalueaftergroup#1{% \xa\xa\xa\gdef\xa\xa\csname moodle@remember@\string#1\endcsname\xa{\xa\def\xa#1\xa{#1}}% \xa\aftergroup\csname moodle@remember@\string#1\endcsname } % \end{macrocode} % % \subsubsection{Main Switch: to create XML or not} % % \begin{macrocode} \long\def\@moodle@ifgeneratexml#1#2{% % If we are generating XML, do #1; otherwise do #2. \tikzifexternalizing{% % This run of LaTeX is currently ONLY generating a Tikz image % to be saved in an external file. We do NOT want to waste time % generating XML, and moreover trying to do so would cause errors % because of file dependencies. #2% }{% \ifmoodle@draftmode #2% \else #1% \fi }% } % \end{macrocode} % % \subsection{Key-Value Pairs for Quiz Questions} % % The various options are set using key-value syntax of |xkeyval|. % \begin{macrocode} \def\moodleset#1{\setkeys{moodle}{#1}}% % \end{macrocode} % We first define some macros that will help us write other macros. % Calling |\generate@moodle@write@code|\marg{name}|<|\meta{HTML tag}|>|\marg{text to write} % creates a macro |\moodle@write|\meta{name}, taking no parameters, % which writes the code |<|\meta{HTML tag}|>...| % 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 % |\generate@moodle@write@code*| passes |#3| verbatim as text. % % For example, % |\generate@moodle@write@code{excuse}{\theexcuse}| % would expand to % \begin{Verbatim}[gobble=4,frame=single] % \gdef\moodle@writeexcuse{% % \xa\def\xa\test@iii\xa{\theexcuse}% % \ifx\test@iii\@moodle@empty % \writetomoodle[2]{ }% % \else % \xa\converttohtmlmacro\xa\moodle@htmltowrite\xa{\theexcuse}% % \writetomoodle[2]{ }% % \writetomoodle[4]{ \moodle@htmltowrite

]]>
}% % \writetomoodle[2]{
}% % \fi % }% % \end{Verbatim} % but % |\generate@moodle@write@code*{excuse}{\theexcuse}| % would expand only to % \begin{Verbatim}[gobble=4,frame=single] % \gdef\moodle@writeexcuse{% % \writetomoodle[2]{ \theexcuse}% % } % \end{Verbatim} % \begin{macrocode} \def\generate@moodle@write@code{% \@ifnextchar*\generate@moodle@write@data\generate@moodle@write@html }% \def\generate@moodle@write@html#1<#2>#3{% % #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">}% \else \xa\converttohtmlmacro\xa\moodle@htmltowrite\xa{#3}% \writetomoodle[2]{ <#2 format="html">}% \writetomoodle[4]{ \moodle@htmltowrite

]]>
}% \writetomoodle[2]{ }% \fi }% }% \def\generate@moodle@write@data*#1<#2>#3{% % #1 = NAME for \moodle@writeNAME % #2 = HTML tag % #3 = what, exactly, to write \xa\gdef\csname moodle@write#1\endcsname{% \writetomoodle[2]{ <#2>#3}% }% }% % \end{macrocode} % \subsubsection{Keys for all question types} % \begin{macrocode} %% QUESTIONNAME \define@cmdkey{moodle}[moodle@]{questionname}{}% \gdef\moodle@writequestionname{% \writetomoodle[2]{}% \writetomoodle[4]{ \moodle@questionname}% \writetomoodle[2]{}% }% %% QUESTIONTEXT %I tried to use questiontext as a key, but it doesn't seem to work. %The trouble is that xkeyval has trouble parsing a key with a \par token followed by a comma within brackets, %like \setkeys{moodle}{questiontext={ABC\par [D,E]}} %It's not worth trying to fix. \long\def\questiontext#1{% %\converttohtmlmacro\myoutput{#1}% %\let\moodle@questiontext=\myoutput% \def\moodle@questiontext{#1}% }% \generate@moodle@write@code{questiontext}{\moodle@questiontext}%{% %% PENALTY FOR WRONG ATTEMPT \define@cmdkey{moodle}[moodle@]{penalty}[0.1000000]{}% \generate@moodle@write@code*{penalty}{\moodle@penalty}% \moodleset{penalty}% %% FEEDBACK % Moodle allows for feedback tailored to each question, % and feedback tailored to each right or wrong answer. % We shall use the key 'feedback' to record both kinds of feedback, % relying on TeX's grouping mechanism to keep them apart. % When it comes time to write them to XML, % \moodle@writegeneralfeedback uses the HTML tag % whereas \moodle@writefeedback uses the tag . % Note that the general feedback is NOT inherited by each answer! \define@cmdkey{moodle}[moodle@]{feedback}[]{}% \generate@moodle@write@code{generalfeedback}{\moodle@feedback}% \generate@moodle@write@code{feedback}{\moodle@feedback}% \moodleset{feedback}% %% DEFAULT GRADE %The next line creates \moodle@defaultgrade, %which is how many points the quiz question is worth. %Key calls like [default grade=7] set \moodle@defaultgrade. \define@cmdkey{moodle}[moodle@]{default grade}[1.0]{}% %Next, makes 'points' a synonym for 'default grade' \define@key{moodle}{points}[1.0]{\xa\def\csname moodle@default grade\endcsname{#1}} \generate@moodle@write@code*{defaultgrade}{\csname moodle@default grade\endcsname}% \moodleset{default grade=1.0} %This sets the default. %% HIDDEN \define@boolkey{moodle}[moodle@]{hidden}[true]{}% \generate@moodle@write@code*{hidden}{\ifmoodle@hidden 1\else 0\fi}% \moodleset{hidden=false}% \def\moodle@writecommondata{% \moodle@writequestionname% \moodle@writequestiontext% \moodle@writedefaultgrade% \moodle@writegeneralfeedback% \moodle@writepenalty% \moodle@writehidden% }% % TO DO: 'tags' (i.e., keywords) % \end{macrocode} % \subsubsection{Keys for all answers} % \begin{macrocode} %% FRACTION -- how much this answer is worth out of 100 percent \define@cmdkey{moodle}[moodle@]{fraction}[100]{}% %We do not create \moodle@writefraction, because the fraction occurs in %the XML within the answer tag, like . \moodleset{fraction=100} %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}{\ifmoodle@single true\else false\fi}% \moodleset{single}% %The key 'multiple' is an antonym to 'single'. \define@boolkey{moodle}[moodle@]{multiple}[true]{\ifmoodle@multiple\moodle@singlefalse\else\moodle@singletrue\fi}% %% SHUFFLE ANSWERS \define@boolkey{moodle}[moodle@]{shuffle}[true]{}% \generate@moodle@write@code*{shuffle}{\ifmoodle@shuffle 1\else 0\fi}% \moodleset{shuffle=true}% %% TO DO: CORRECTFEEDBACK %% TO DO: PARTIALLYCORRECTFEEDBACK %% TO DO: INCORRECTFEEDBACK %% TO DO: 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]{% \def\moodle@numbering{#1}% \def\test@@i{#1}% \ifx\test@@i\@moodle@alpha \def\moodle@numbering{abc}\fi \ifx\test@@i\@moodle@alph \def\moodle@numbering{abc}\fi \ifx\test@@i\@moodle@Alpha \def\moodle@numbering{ABC}\fi \ifx\test@@i\@moodle@Alph \def\moodle@numbering{ABC}\fi \ifx\test@@i\@moodle@arabic \def\moodle@numbering{123}\fi \ifx\test@@i\@moodle@roman \def\moodle@numbering{iii}\fi \ifx\test@@i\@moodle@Roman \def\moodle@numbering{IIII}\fi }% %'answer numbering' will be a synonym to 'numbering' \define@key{moodle}{answer numbering}[abc]{\setkeys{moodle}{numbering={#1}}}% \generate@moodle@write@code*{answernumbering}{\moodle@numbering}% %N.B. if we did not set the default here, then \moodle@numbering would be undefined, causing problems. \moodleset{answer numbering=abc}% \def\@moodle@alpha{alpha}% \def\@moodle@Alpha{Alpha}% \def\@moodle@alph{alph}% \def\@moodle@Alph{Alph}% \def\@moodle@arabic{arabic}% \def\@moodle@roman{roman}% \def\@moodle@Roman{Roman}% \def\@moodle@abc{abc}% \def\@moodle@ABC{ABC}% \def\@moodle@arabicnumbers{123}% \def\@moodle@iii{iii}% \def\@moodle@IIII{IIII}% \def\@moodle@none{none}% \def\moodle@obeynumberingstyle{% \ifx\moodle@numbering\@moodle@abc \renewcommand\theenumii{\alph{enumii}}% \fi \ifx\moodle@numbering\@moodle@ABC \renewcommand\theenumii{\Alph{enumii}}% \fi \ifx\moodle@numbering\@moodle@arabicnumbers \renewcommand\theenumii{\arabic{enumii}}% \fi \ifx\moodle@numbering\@moodle@iii \renewcommand\theenumii{\roman{enumii}}% \fi \ifx\moodle@numbering\@moodle@IIII \renewcommand\theenumii{\Roman{enumii}}% \fi \ifx\moodle@numbering\@moodle@none \renewcommand\labelenumii{$\bullet$~}% \fi } %TO DO: * 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. %% DISPLAY MODE -- affects Cloze multiple choice questions only. % 0 = inline, 1 = vertical, 2 = horizontal \def\moodle@multi@mode{0}% \define@key{moodle}{inline}[]{\def\moodle@multi@mode{0}}% \define@key{moodle}{vertical}[]{\def\moodle@multi@mode{1}}% \define@key{moodle}{horizontal}[]{\def\moodle@multi@mode{2}}% % \end{macrocode} % \subsubsection{Keys for numerical questions} % \begin{macrocode} %% TOLERANCE \define@cmdkey{moodle}[moodle@]{tolerance}[0]{}% \moodleset{tolerance=0}% %There is no \moodle@writetolerance, because in the XML the %tolerance is given within the answer tag, %like . % TO DO: implement unit-handling for numerical questions! % \end{macrocode} % \subsubsection{Keys for short answer questions} % \begin{macrocode} %% CASE SENSITIVE \define@boolkey{moodle}[moodle@]{case sensitive}[true]{}% \generate@moodle@write@code*{usecase}{\csname ifmoodle@case sensitive\endcsname 1\else 0\fi}% % We make 'usecase' a synonym for 'case sensitive'. \define@boolkey{moodle}[moodle@]{usecase}[true]{\ifmoodle@usecase\csname moodle@case sensitivetrue\endcsname\else\csname moodle@case sensitivefalse\endcsname\fi}% \moodleset{case sensitive=false}% % \end{macrocode} % \subsubsection{Keys for matching questions} % \begin{macrocode} %% DRAG-AND-DROP FORMAT \define@boolkey{moodle}[moodle@]{draganddrop}[true]{}% % We make 'dd' and 'dragdrop' and 'drag and drop' synonyms for 'draganddrop'. \define@boolkey{moodle}[moodle@]{dd}[true]{\ifmoodle@dd\moodle@draganddroptrue\else\moodle@draganddropfalse\fi}% \define@boolkey{moodle}[moodle@]{drag and drop}[true]{\moodle@ddsynonym}% \def\moodle@ddsynonym{% \csname ifmoodle@drag and drop\endcsname \moodle@draganddroptrue \else \moodle@draganddropfalse \fi } \moodleset{draganddrop=false}% % \end{macrocode} % \subsubsection{Keys for essay questions} % \begin{macrocode} %% EDITOR \def\@moodle@html{html}% \def\@moodle@htmlfile{html+file}% \def\@moodle@text{text}% \def\@moodle@plain{plain}% \def\@moodle@monospaced{monospaced}% \def\@moodle@file{file}% \def\@moodle@noinline{noinline}% \define@choicekey{moodle}{response format}% {html,html+file,text,monospaced,file}[html]% {\def\test@i{#1}% \ifx\test@i\@moodle@html % HTML Editor \def\moodle@responseformat{editor}% \fi \ifx\test@i\@moodle@htmlfile % HTML Editor with File Picker \def\moodle@responseformat{editorfilepicker}% \fi \ifx\test@i\@moodle@text % Plain text \def\moodle@responseformat{plain}% \fi \ifx\test@i\@moodle@plain % Plain text \def\moodle@responseformat{plain}% \fi \ifx\test@i\@moodle@monospaced % Plain text, monospaced font \def\moodle@responseformat{monospaced}% \fi \ifx\test@i\@moodle@file % No inline text (i.e., attachments only) \def\moodle@responseformat{noinline}% \fi \ifx\test@i\@moodle@noinline % No inline text (i.e., attachments only) \def\moodle@responseformat{noinline}% \fi }% \generate@moodle@write@code*{responseformat}{\moodle@responseformat}% \moodleset{response format=html}% %N.B. if we did not set a default, then \moodle@responseformat would be undefined, causing problems. %% RESPONSE REQUIRED \define@boolkey{moodle}[moodle@]{response required}[true]{}% % TO DO: Make synonym 'required' \generate@moodle@write@code*{responserequired}{\csname ifmoodle@response required\endcsname 1\else 0\fi}% \moodleset{response required=false}% %% RESPONSEFIELDLINES \define@cmdkey{moodle}[moodle@]{response field lines}[15]{}% \generate@moodle@write@code*{responsefieldlines}{\csname moodle@response field lines\endcsname}% %Make synonyms 'input box size' or 'height' or 'lines'? \moodleset{response field lines=15}% N.B. if we do not set a default, then \moodle@responseformat will be undefined, causing problems. %% ATTACHMENTS ALLOWED \def\@moodle@unlimited{unlimited}% \define@choicekey{moodle}{attachments allowed}{0,1,2,3,unlimited}[1]{% \def\test@i{#1}% \ifx\test@i\@moodle@unlimited \def\moodle@attachmentsallowed{-1}% \else \def\moodle@attachmentsallowed{#1}% \fi } \generate@moodle@write@code*{attachmentsallowed}{\moodle@attachmentsallowed} \moodleset{attachments allowed=0}% %% ATTACHMENTS REQUIRED \define@choicekey{moodle}{attachments required}{0,1,2,3}[1]{\def\moodle@attachmentsrequired{#1}}% \generate@moodle@write@code*{attachmentsrequired}{\moodle@attachmentsrequired} \moodleset{attachments required=0}% %% RESPONSE TEMPLATE \define@key{moodle}{template}{\long\def\moodle@responsetemplate{#1}}% \generate@moodle@write@code{responsetemplate}{\moodle@responsetemplate} \moodleset{template={}}% % \end{macrocode} % \subsubsection{Hint tags} % The following are not yet fully implemented. % \begin{macrocode} %% SHOWNUMCORRECT \define@boolkey{moodle}[moodle@]{shownumcorrect}[true]{}% \gdef\moodle@writeshownumcorrect{% \if\moodle@shownumcorrect \writetomoodle[4]{ }% \fi }% \moodleset{shownumcorrect=false}% %% CLEARWRONG \define@boolkey{moodle}[moodle@]{clearwrong}[true]{}% \gdef\moodle@writeclearwrong{% \if\moodle@clearwrong \writetomoodle[4]{ }% \fi }% \moodleset{clearwrong=false}% % TO DO: Implement hints % \end{macrocode} % % \subsection{Answer handling} % % \begin{macrocode} %The Answers XML depends heavily on the question type. %Each type of question defines how it obtains answers from the LaTeX input, %how it typesets those in a PDF or DVI, and how it writes them as XML code. %It will write that XML to the macro \moodle@answers@xml, %which them gets written to the file when \moodle@writeanswers %is invoked. \def\moodle@answers@xml{}% \gdef\moodle@writeanswers{% \writetomoodle{\moodle@answers@xml}% }% \newcommand\addto@xml[3][0]{% % #1 = spaces to indent (default=0) % #2 = macro containing XML code (possibly empty) % #3 = XML text to be appended to that macro (will be \edef'd) \calculateindent{#1}% \edef\xml@to@add{\moodle@indent\trim@pre@space{#3}}% \ifx#2\@moodle@empty \edef\newxml{\noexpand#2\xml@to@add}% \else \edef\newxml{\noexpand#2^^J\xml@to@add}% \fi \xa\xa\xa\def\xa\xa\xa#2\xa\xa\xa{\newxml}% }% % \end{macrocode} % % \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 %%%%%%%%%%%%%%%%%%%%%%%%%% % TODO: I don't think I really want to handle this. Not now. % \end{macrocode} % \subsection{Front Ends} % This section creates the user interface for the various question types. % First, we define a generic command to create % a front-end environment for a Moodle question type. % In order to function, the following macros must be hard-coded: % \begin{itemize} % \item |\moodle@|\meta{type}|@latexprocessing|: % Loops through the saved |\item|'s to typeset them in LaTeX, % usually inside an itemize or enumerate environment. % \item |\save|\meta{type}|answer#1|: % Processes the text of a single |\item| to save the information to memory, % usually inside |\moodle@answers@xml|. % \item |\write|\meta{type}|question|: % Writes the information, hitherto saved only in macros, % into the XML file. % \end{itemize} % For example, to create the `shortanswer' question type, % we shall call % \begin{Verbatim}[gobble=5,frame=single] % \moodle@makefrontend{shortanswer} % \def\moodle@shortanswer@latexprocessing{...} % \def\saveshortansweranswer#1{...} % \def\writeshortanswerquestion{...} % \end{Verbatim} % % \begin{macrocode} \def\moodle@makefrontend#1{% \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 \noindent \moodle@questionheader \csname moodle@#1@latexprocessing\endcsname %Now, writing information to XML \@moodle@ifgeneratexml{% \xa\questiontext\xa{\moodle@questionheader}% Save the question text. \bgroup \gdef\moodle@answers@xml{}% \setkeys{moodle}{feedback={}}% \xa\loopthroughitemswithcommand\xa{\csname save#1answer\endcsname}% \passvalueaftergroup{\moodle@answers@xml}% \egroup \csname write#1question\endcsname }{}% \egroup }% } % \end{macrocode} % % \subsubsection{Essay Question Front-End} % The essay question is the only question type whose front end % is not yet created by |\moodle@makefrontend|. % This is because of what it must do with its |\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. \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 } \NewEnviron{essay}[2][]{% \bgroup \setkeys{moodle}{#1,questionname={#2}}% \expandafter\gatheritems\expandafter{\BODY}% \let\moodle@questionheader=\gatheredheader %First, the LaTeX processing. \item \textbf{\moodle@questionname}\par \noindent \moodle@questionheader \csname moodle@essay@latexprocessing\endcsname %Now, writing information to memory. \@moodle@ifgeneratexml{% \xa\questiontext\xa{\moodle@questionheader}% Save the question text. \bgroup \gdef\moodle@answers@xml{}% % \ifnum\c@numgathereditems=0\relax \addto@xml[2]\moodle@answers@xml{}% \else \addto@xml[2]\moodle@answers@xml{1\relax \addto@xml[4]\moodle@answers@xml{
    }% \fi \loopthroughitemswithcommand{\moodle@savegraderinfo}% \ifnum\c@numgathereditems>1\relax \addto@xml[4]\moodle@answers@xml{
}% \fi \addto@xml[2]\moodle@answers@xml{]]>
}% \fi % \passvalueaftergroup{\moodle@answers@xml}% \egroup \writeessayquestion }{}% \egroup }% %%%% TO DO %%%% To make essay work will be tough. %%%% Every line from \ifnum\c@numgathereditems=0\relax through its \else and \fi, %%%% with the exception of %%%% \xa\loopthroughitemswithcommand\xa{\csname save#1answer\endcsname}% %%%% , does not exist in our current \moodle@makefrontend code. %%%% How can we cope? %%%% %%%% Idea: change \moodle@makefrontend so that %%%% 1. if \c@numgathereditems=0, we don't do anything. %%%% 2. it calls a preamble and postamble around the \loopthroughitemswithcommand. %%%% Like this: %%%% %%%% \@moodle@ifgeneratexml{% %%%% \xa\questiontext\xa{\moodle@questionheader}% Save the question text. %%%% \bgroup %%%% \gdef\moodle@answers@xml{}% %%%% \setkeys{moodle}{feedback={}}% %%%% \@ifundefined{moodle@#1@answers@preamble}{}{}% %%%% \csname moodle@#1@answers@preamble\endcsname %%%% \ifnum\c@numgathereditems=0\relax %%%% \relax %%%% \else %%%% \xa\loopthroughitemswithcommand\xa{\csname save#1answer\endcsname}% %%%% \fi %%%% \@ifundefined{moodle@#1@answers@postamble}{}{}% %%%% \csname moodle@#1@answers@postamble\endcsname %%%% \passvalueaftergroup{\moodle@answers@xml}% %%%% \egroup %%%% \csname write#1question\endcsname %%%% }{}% %%%% The \@ifundefined lines should automatically define the %%%% \...@preamble \...@postamble macros to be \relax if they don't exist already. \gdef\writeessayquestion{% \writetomoodle{}% \moodle@writecommondata% \moodle@writeresponserequired% \moodle@writeresponseformat% \moodle@writeresponsefieldlines% \moodle@writeattachmentsallowed% \moodle@writeattachmentsrequired% \moodle@writeanswers% The 'answers' XML really contains the grader info. \moodle@writeresponsetemplate% \writetomoodle{}% }% \def\moodle@print@essay@answer#1{% \item #1% }% \def\moodle@savegraderinfo#1{% \bgroup \moodle@savegraderinfo@int#1\moodle@answer@rdelim \passvalueaftergroup{\moodle@answers@xml}% \egroup }% \newcommand\moodle@savegraderinfo@int[1][]{% \setkeys{moodle}{fraction=0,#1}% \moodle@savegraderinfo@int@int% }% \def\moodle@savegraderinfo@int@int#1\moodle@answer@rdelim{% \xa\converttohtmlmacro\xa\moodle@answertext@html\xa{#1}% %\trim@spaces@in\moodle@answertext \ifnum\c@numgathereditems>1\relax \addto@xml[6]{\moodle@answers@xml}{
  • \moodle@answertext@html
  • }% \else \addto@xml[4]{\moodle@answers@xml}{\moodle@answertext@html}% \fi }% % \end{macrocode} % % \subsubsection{Short Answer Question Front-End} % % \begin{macrocode} \def\blank{\rule{1in}{0.5pt}}% % TO DO: 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][]{% % \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 % \noindent % \moodle@questionheader % \csname moodle@shortanswer@latexprocessing\endcsname % %Now, writing information to memory. % \@moodle@ifgeneratexml{% % \xa\questiontext\xa{\moodle@questionheader}% Save the question text. % \bgroup % \gdef\moodle@answers@xml{}% % \setkeys{moodle}{feedback={}}% % \xa\loopthroughitemswithcommand\xa{\csname saveshortansweranswer\endcsname}% % \passvalueaftergroup{\moodle@answers@xml}% % \egroup % \csname writeshortanswerquestion\endcsname % }{}% % \egroup % }% \moodle@makefrontend{shortanswer}% % LATEX PROCESSING \def\moodle@shortanswer@latexprocessing{% \begin{itemize} \setlength\itemsep{0pt}\setlength\parskip{0pt}% \loopthroughitemswithcommand{\moodle@print@shortanswer@answer}% \end{itemize}% } \def\moodle@print@shortanswer@answer#1{% \moodle@print@shortanswer@answer@int#1\@rdelim }% \newcommand\moodle@print@shortanswer@answer@int[1][]{% \setkeys{moodle}{#1}% \moodle@print@shortanswer@answer@int@int% }% \def\moodle@print@shortanswer@answer@int@int#1\@rdelim{% \ifx\moodle@fraction\@hundred \item #1\quad$\checkmark$% \else \item #1\quad (\moodle@fraction\%)% \fi }% % SAVING ANSWERS TO MEMORY \def\saveshortansweranswer#1{% \bgroup \saveshortansweranswer@int#1\moodle@answer@rdelim \passvalueaftergroup{\moodle@answers@xml}% \egroup }% \newcommand\saveshortansweranswer@int[1][]{% \setkeys{moodle}{fraction=100,#1}% %%%%%% DEFAULT VALUE IS 100% \saveshortansweranswer@int@int% }% \def\saveshortansweranswer@int@int#1\moodle@answer@rdelim{% \def\moodle@answertext{#1}% \addto@xml[2]{\moodle@answers@xml}{}% \addto@xml[4]{\moodle@answers@xml}{ \moodle@answertext}% \ifx\moodle@feedback\@empty\relax\else \addto@xml[4]{\moodle@answers@xml}{ \moodle@feedback

    ]]>
    }% \fi \addto@xml[2]{\moodle@answers@xml}{
    }% }% % WRITING QUESTION TO XML FILE \gdef\writeshortanswerquestion{% \writetomoodle{}% \moodle@writecommondata% \moodle@writeusecase% \moodle@writeanswers% \writetomoodle{}% }% % \end{macrocode} % % \subsubsection{Numerical Question Front-End} % % \begin{macrocode} \moodle@makefrontend{numerical}% % LATEX PROCESSING \def\moodle@numerical@latexprocessing{% \begin{itemize} \setlength\itemsep{0pt}\setlength\parskip{0pt}% \loopthroughitemswithcommand{\moodle@print@numerical@answer}% \end{itemize}% } \def\moodle@print@numerical@answer#1{% \moodle@print@numerical@answer@int#1\@rdelim }% \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{}% \else \edef\moodle@numericalprint@tolerance{\noexpand\pm\moodle@tolerance}% \fi \ifx\moodle@fraction\@hundred \item $#1\moodle@numericalprint@tolerance\quad\checkmark$% \else \item $#1\moodle@numericalprint@tolerance$\quad (\moodle@fraction\%)% \fi }% % SAVING ANSWERS TO MEMORY \def\savenumericalanswer#1{% \bgroup \savenumericalanswer@int#1\moodle@answer@rdelim \passvalueaftergroup{\moodle@answers@xml}% \egroup }% \newcommand\savenumericalanswer@int[1][]{% \setkeys{moodle}{fraction=100,#1}% %%%%%% DEFAULT VALUE IS 100% \savenumericalanswer@int@int% }% \def\savenumericalanswer@int@int#1\moodle@answer@rdelim{% \def\moodle@answertext{#1}% \addto@xml[2]{\moodle@answers@xml}{}% \addto@xml[4]{\moodle@answers@xml}{ \moodle@answertext}% \addto@xml[4]{\moodle@answers@xml}{ \moodle@tolerance}% \ifx\moodle@feedback\@empty\relax\else \addto@xml[4]{\moodle@answers@xml}{ \moodle@feedback

    ]]>
    }% \fi \addto@xml[2]{\moodle@answers@xml}{
    }% }% % WRITING QUESTION TO XML FILE \gdef\writenumericalquestion{% \writetomoodle{}% \moodle@writecommondata% \moodle@writeanswers% \writetomoodle{}% }% % \end{macrocode} % % \subsubsection{Multiple Choice Question Front-End} % % \begin{macrocode} %Multiple choice has the structure % \begin{multi}[options]{name}% % What is 5+7? % \item 13 % \item* 12 % \item 11 % \end{multi}% \moodle@makefrontend{multi} % LATEX PROCESSING \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}% } \def\moodle@print@multichoice@answer#1{% \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 }% \def\moodle@print@multichoice@answer@int@int#1#2\@rdelim{% \def\test@i{#1}% \ifmoodle@single \ifx\test@i\@star \item #2$~\checkmark$% \else \item #1#2% \fi \else \ifx\test@i\@star \item #2% \setkeys{moodle}{fraction=\moodle@autopoints}% \else \item #1#2% \fi $~(\moodle@fraction\%)$ \fi }% % COMMON UTILITY: COUNTING CORRECT ANSWERS \newcounter{moodle@numcorrectanswers}% \newlength{\moodle@pointspercorrect}% \def\moodle@countcorrectanswers{% \setcounter{moodle@numcorrectanswers}{0}% \global\setlength{\moodle@pointspercorrect}{100pt}% \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}% \else \global\divide\moodle@pointspercorrect by \c@moodle@numcorrectanswers\relax \edef\moodle@autopoints{\strip@pt\moodle@pointspercorrect}% \fi } \def\moodle@countcorrectanswers@a#1{% %The grouping is to keep key answer-specific key changes local. \bgroup \moodle@countcorrectanswers@b#1\moodle@answer@rdelim \egroup }% \newcommand\moodle@countcorrectanswers@b[1][]{% \setkeys{moodle}{fraction=0,#1}% \moodle@countcorrectanswers@c% }% \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}% \fi }% \def\moodle@setautopoints#1pt{% \gdef\moodle@autopoints{#1}% } % 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 \passvalueaftergroup{\moodle@answers@xml}% \egroup }% \newcommand\savemultianswer@int[1][]{% \setkeys{moodle}{fraction=0,#1}% \savemultianswer@int@int% }% \def\savemultianswer@int@int#1#2\moodle@answer@rdelim{% \def\test@i{#1}% \ifx\test@i\@star \ifmoodle@single \setkeys{moodle}{fraction=100}% \else \setkeys{moodle}{fraction=\moodle@autopoints}% \fi \def\moodle@answertext{#2}% \else \def\moodle@answertext{#1#2}% \fi \trim@spaces@in\moodle@answertext \addto@xml[2]{\moodle@answers@xml}{}% \xa\converttohtmlmacro\xa\moodle@answertext@html\xa{\moodle@answertext}% \addto@xml[4]{\moodle@answers@xml}{ \moodle@answertext@html

    ]]>
    }% \ifx\moodle@feedback\@empty\relax\else \addto@xml[4]{\moodle@answers@xml}{ \moodle@feedback

    ]]>
    }% \fi \addto@xml[2]{\moodle@answers@xml}{
    }% }% % WRITING QUESTION TO XML FILE \gdef\writemultiquestion{% \writetomoodle{}% \moodle@writecommondata% \moodle@writesingle% \moodle@writeshuffle% \moodle@writeanswernumbering% \moodle@writeanswers% \writetomoodle{}% }% % \end{macrocode} % % \subsubsection{Matching Question Front-End} % % \begin{macrocode} \let\answer=\hfill \moodle@makefrontend{matching} % LATEX PROCESSING \def\moodle@matching@latexprocessing{% \bgroup \let\answer=\hfill \begin{enumerate}\renewcommand{\theenumi}{\alph{enumi}}\setlength\itemsep{0pt}\setlength\parskip{0pt}% \loopthroughitemswithcommand{\moodle@print@matching@answer}% \end{enumerate}% \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\relax }% \long\def\moodle@print@matching@answer@int@int#1\answer#2\@rdelim{% \item #1\hfill #2% }% % SAVING ANSWERS TO MEMORY \long\def\savematchinganswer#1{% \bgroup \savematchinganswer@int#1\moodle@answer@rdelim% \passvalueaftergroup{\moodle@answers@xml}% \egroup }% \newcommand\savematchinganswer@int[1][]{% \setkeys{moodle}{#1}% \xa\savematchinganswer@int@int\space% }% \long\def\savematchinganswer@int@int#1\answer#2\moodle@answer@rdelim{% % Note that #1 may simply be \relax. \def\moodle@subquestiontext{#1}% \def\moodle@subanswertext{#2}% \trim@spaces@in\moodle@subquestiontext \xa\converttohtmlmacro\xa\moodle@subquestiontext@htmlized\xa{\moodle@subquestiontext}% \trim@spaces@in\moodle@subanswertext \ifmoodle@draganddrop \xa\converttohtmlmacro\xa\moodle@subanswertext@htmlized\xa{\moodle@subanswertext}% \fi \addto@xml[2]{\moodle@answers@xml}{}% \ifx\moodle@subquestiontext\@empty \addto@xml[4]{\moodle@answers@xml}{ }% \else \addto@xml[4]{\moodle@answers@xml}{ \moodle@subquestiontext@htmlized

    ]]>
    }% \fi \ifmoodle@draganddrop % \show\moodle@subanswertext@htmlized \addto@xml[4]{\moodle@answers@xml}{ \moodle@subanswertext@htmlized

    ]]>
    }% \else % \show\moodle@subanswertext \addto@xml[4]{\moodle@answers@xml}{ \moodle@subanswertext}% \fi \ifx\moodle@feedback\@empty\relax\else \addto@xml[4]{\moodle@answers@xml}{ \moodle@feedback

    ]]>
    }% \fi \addto@xml[2]{\moodle@answers@xml}{
    }% }% % WRITING QUESTION TO XML FILE \gdef\writematchingquestion{% \ifmoodle@draganddrop \writetomoodle{}% \else \writetomoodle{}% \fi \moodle@writecommondata% \moodle@writesingle% \moodle@writeshuffle% \moodle@writeanswernumbering% \moodle@writeanswers% \writetomoodle{}% % \gdef\moodle@answ }% % \end{macrocode} % % \subsection{Cloze Questions} % Because cloze questions are so complicated, they get their own section of code. % The cloze strategy is as follows. % % All subquestions show up as part of the question body text. % For each type of subquestion, we have a cloze-version environment % that actually has 2 versions, depending on whether we are doing LaTeX or XML processing. % So the main environment is quite typical: % \begin{enumerate} % \item Process the body as LaTeX. % During this run, a |\begin{multi}| etc.~will be processed for display onscreen. % \item Then save the body as the questiontext for XML. % During this run, a |\begin{multi}| etc.~will be parsed and turned into % cloze code as part of the XML questiontext. % \end{enumerate} % \begin{macrocode} % LATEX PROCESSING % SAVING ANSWERS TO MEMORY \NewEnviron{cloze}[2][]{% \bgroup \setkeys{moodle}{default grade=1}% \setkeys{moodle}{#1,questionname={#2}}% % 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 \noindent \BODY %Now, writing information to memory. \@moodle@ifgeneratexml{% \xa\questiontext\xa{\BODY}% Save the question text as HTML. \writeclozequestion }{}% \egroup% } \def\moodle@enableclozeenvironments{% \let\multi=\clozemulti \let\endmulti=\endclozemulti \let\numerical=\clozenumerical \let\endnumerical=\endclozenumerical \let\shortanswer=\clozeshortanswer \let\endshortanswer=\endclozeshortanswer } % WRITING QUESTION TO XML FILE \gdef\writeclozequestion{% \writetomoodle{}% \moodle@writecommondata% \writetomoodle{}% }% % \end{macrocode} % % \subsubsection{Cloze Multiple Choice Questions} % % \begin{macrocode} % TODO: Guard against [single=false] option \NewEnviron{clozemulti}[1][]{% \bgroup \setkeys{moodle}{default grade=1}% \setkeys{moodle}{#1}% \expandafter\gatheritems\xa{\BODY}% \let\moodle@questionheader=\gatheredheader \ifhtmlizer@active %HTML version \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:}% \ifcase\moodle@multi@mode\relax % Case 0: dropdown box style \g@addto@macro{\clozemulti@coding}{MULTICHOICE:}% \or % Case 1: vertical style \g@addto@macro{\clozemulti@coding}{MULTICHOICE_V:}% \else % Case 2: horizontal radio buttons \g@addto@macro{\clozemulti@coding}{MULTICHOICE_H:}% \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\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. \def\cloze@multichoice@table@text{}% \loopthroughitemswithcommand{\moodle@print@clozemultichoice@answer}% \ifcase\moodle@multi@mode\relax %Case 0: dropdown box style \begin{tabular}[t]{|l|}\firsthline% (\firsthline is from the array package.) \cloze@multichoice@table@text% \end{tabular}% \or %Case 1: vertical style \begin{itemize}\setlength\itemsep{0pt}\setlength\parskip{0pt}% \cloze@multichoice@table@text% \end{itemize}% \else %Case 2: horizontal radio buttons \par{\cloze@multichoice@table@text}\par% \fi \def\endclozemulti@code{\relax}% \fi \passvalueaftergroup\endclozemulti@code% \passvalueaftergroup\htmlize@afteraction@hook% \egroup% }[\endclozemulti@code]% \def\moodle@print@clozemultichoice@answer#1{% \moodle@print@clozemultichoice@answer@int#1\@rdelim% }% \newcommand\moodle@print@clozemultichoice@answer@int[1][]{% \moodle@print@clozemultichoice@answer@int@int% }% \def\moodle@print@clozemultichoice@answer@int@int#1#2\@rdelim{% % 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 \else \g@addto@macro\cloze@multichoice@table@text{$\bullet~$}% Case 2 \fi \def\test@i{#1}% \ifx\test@i\@star \g@addto@macro\cloze@multichoice@table@text{#2$~\checkmark$}% \else \g@addto@macro\cloze@multichoice@table@text{#1#2}% \fi \ifcase\moodle@multi@mode\relax \g@addto@macro{\cloze@multichoice@table@text}{\\ \hline}% Case 0 \or \relax% Case 1 \else \g@addto@macro{\cloze@multichoice@table@text}{\hfill}% Case 2 \fi }% \def\saveclozemultichoiceanswer#1{% \bgroup \saveclozemultichoiceanswer@int#1\moodle@answer@rdelim \egroup }% \newcommand\saveclozemultichoiceanswer@int[1][]{% \setkeys{moodle}{fraction=0,#1}% \saveclozemultichoiceanswer@int@int% }% \def\saveclozemultichoiceanswer@int@int#1#2\moodle@answer@rdelim{% \def\test@i{#1}% \ifgatherbeginningofloop\else \xa\gdef\xa\clozemulti@coding\xa{\clozemulti@coding\clozetilde}% separator between answers \fi \ifx\test@i\@star \setkeys{moodle}{fraction=100}% \def\moodle@answertext{#2}% \else \def\moodle@answertext{#1#2}% \fi \trim@spaces@in\moodle@answertext \ifx\moodle@fraction\@hundred \g@addto@macro\clozemulti@coding{=}% \fi \ifnum0<\moodle@fraction\relax \ifnum\moodle@fraction<100\relax \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}% \fi }% % \end{macrocode} % % \subsubsection{Cloze Numerical Questions} % % \begin{macrocode} \NewEnviron{clozenumerical}[1][]{% \bgroup \expandafter\gatheritems\expandafter{\BODY}% \let\moodle@questionheader=\gatheredheader \setkeys{moodle}{default grade=1}% \setkeys{moodle}{#1}% \ifhtmlizer@active %HTML version \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:}% \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\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}% \def\endclozenumerical@code{\relax}% \fi \passvalueaftergroup\endclozenumerical@code% \passvalueaftergroup\htmlize@afteraction@hook% \egroup }[\endclozenumerical@code]% \def\moodle@print@clozenumerical@answer#1{% \bgroup \moodle@print@clozenumerical@answer@int#1\@rdelim \egroup }% \newcommand\moodle@print@clozenumerical@answer@int[1][]{% \setkeys{moodle}{#1}% \moodle@print@clozenumerical@answer@int@int% }% \def\moodle@print@clozenumerical@answer@int@int#1\@rdelim{% \ifx\moodle@fraction\@hundred \def\moodle@clozenumericalprint@fraction{$\checkmark$}% \else \edef\moodle@clozenumericalprint@fraction{(\moodle@fraction\%)}% \fi \ifdim0pt=\moodle@tolerance pt\relax \def\moodle@clozenumericalprint@tolerance{}% \else \edef\moodle@clozenumericalprint@tolerance{\noexpand\pm\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 \\}% }% \def\saveclozenumericalanswer#1{% \bgroup \saveclozenumericalanswer@int#1\moodle@answer@rdelim \egroup }% \newcommand\saveclozenumericalanswer@int[1][]{% \setkeys{moodle}{fraction=100,#1}% %%%%%% DEFAULT VALUE IS 100% \saveclozenumericalanswer@int@int% }% \def\saveclozenumericalanswer@int@int#1\moodle@answer@rdelim{% \ifgatherbeginningofloop\else \xa\gdef\xa\clozenumerical@coding\xa{\clozenumerical@coding\clozetilde}% separator between answers \fi \def\moodle@answertext{#1}% \trim@spaces@in\moodle@answertext \ifx\moodle@fraction\@hundred \g@addto@macro\clozenumerical@coding{=}% \fi \ifnum0<\moodle@fraction\relax \ifnum\moodle@fraction<100\relax \xdef\clozenumerical@coding{\expandonce\clozenumerical@coding\otherpercent\moodle@fraction\otherpercent}% \fi \fi \xdef\clozenumerical@coding{\expandonce\clozenumerical@coding\moodle@answertext:\moodle@tolerance}% \ifx\moodle@feedback\@empty\else \xdef\clozenumerical@coding{\expandonce\clozenumerical@coding\otherbackslash\otherhash\moodle@feedback}% \fi }% % \end{macrocode} % % \subsubsection{Cloze Short Answer Questions} % % \begin{macrocode} \NewEnviron{clozeshortanswer}[1][]{% \bgroup \expandafter\gatheritems\expandafter{\BODY}% \let\moodle@questionheader=\gatheredheader \setkeys{moodle}{default grade=1}% \setkeys{moodle}{#1}% %Because nesting conditionals built by \csname doesn't work well, %we'll test '\ifmoodle@case sensitive' now and save the result in \count0. \csname ifmoodle@case sensitive\endcsname \count0=1\relax \else \count0=0\relax \fi \ifhtmlizer@active %HTML version \def\moodle@clozeshortanswer@output{}% \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:}% \else \edef\clozeshortanswer@coding{\otherlbrace\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\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)}}% \fi \begin{tabular}[t]{|ll|}\firsthline \cloze@shortanswer@table@text\hline% \end{tabular}% \def\endclozeshortanswer@code{\relax}% \fi \passvalueaftergroup\endclozeshortanswer@code% \passvalueaftergroup\htmlize@afteraction@hook% \egroup }[\endclozeshortanswer@code]% \def\moodle@print@clozeshortanswer@answer#1{% \bgroup \moodle@print@clozeshortanswer@answer@int#1\@rdelim \egroup }% \newcommand\moodle@print@clozeshortanswer@answer@int[1][]{% \setkeys{moodle}{#1}% \moodle@print@clozeshortanswer@answer@int@int% }% \def\moodle@print@clozeshortanswer@answer@int@int#1\@rdelim{% \ifx\moodle@fraction\@hundred \def\moodle@clozeshortanswerprint@fraction{$\checkmark$}% \else \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 \\}% }% \def\saveclozeshortansweranswer#1{% \bgroup \saveclozeshortansweranswer@int#1\moodle@answer@rdelim \egroup }% \newcommand\saveclozeshortansweranswer@int[1][]{% \setkeys{moodle}{fraction=100,#1}% %%%%%% DEFAULT VALUE IS 100% \saveclozeshortansweranswer@int@int% }% \def\saveclozeshortansweranswer@int@int#1\moodle@answer@rdelim{% \ifgatherbeginningofloop\else \xa\gdef\xa\clozeshortanswer@coding\xa{\clozeshortanswer@coding\clozetilde}% separator between answers \fi \def\moodle@answertext{#1}% \trim@spaces@in\moodle@answertext \ifx\moodle@fraction\@hundred \g@addto@macro\clozeshortanswer@coding{=}% \fi \ifnum0<\moodle@fraction\relax \ifnum\moodle@fraction<100\relax \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}% \fi }% % \end{macrocode} % % \section{Converting \LaTeX\ to HTML} % A lot of work must now be done to convert the \LaTeX\ code % of a question or answer into HTML code with embedded \TeX\ for math. % % \subsection{Catcode Setup} % First, we create versions of the special characters with catcode 12, ``other.'' % \begin{macrocode} {\catcode`\#=12\gdef\otherhash{#}% \catcode`\~=12\gdef\othertilde{~}% \catcode`\&=12\gdef\otherampersand{&}% \catcode`\^=12\gdef\othercaret{^}% \catcode`\$=12\gdef\otherdollar{$}% \catcode`\%=12\gdef\otherpercent{%}}% {\catcode`\ =12\gdef\otherspace{ }}% {\tt\catcode`\|=0\catcode`\\=12\relax|gdef|otherbackslash{\}}% {\catcode`\[=1\catcode`\]=2\catcode`\{=12\catcode`\}=12% \gdef\otherlbrace[{]\gdef\otherrbrace[}]] \edef\@otherlbrace{\otherlbrace}% \edef\@otherrbrace{\otherrbrace}% \edef\@otherdollar{\otherdollar}% \edef\@otherbackslash{\otherbackslash}% \edef\@othertilde{\othertilde}% % \end{macrocode} % % Next, we define commands to change catcodes to a suitable verbatim mode % for transcription. % % \begin{macrocode} { \catcode`\[=1\relax \catcode`\]=2\relax \catcode`\|=0\relax |gdef|verbcatcodesweirdest[ |catcode`\{=12|relax |catcode`\}=12|relax |catcode`\\=12|relax ]% }% \def\verbcatcodes{% \catcode`\$=12\relax \catcode`\&=12\relax \catcode`\#=12\relax \catcode`\^=12\relax \catcode`\_=12\relax \catcode`\~=12\relax \makeatletter \catcode`\%=12\relax \catcode`\ =12\relax\catcode\newlinechar=12\verbcatcodesweirdest}% \def\normalcatcodes{% \catcode`\\=0\relax \catcode`\{=1\relax \catcode`\}=2\relax \catcode`\$=3\relax \catcode`\&=4\relax \catcode\endlinechar=5\relax \catcode`\#=6\relax \catcode`\^=7\relax \catcode`\_=8\relax \catcode`\ =10\relax \makeatletter% We will be detokenizing and retokenizing internal control sequences, so we need this. \catcode`\~=13\relax \catcode`\%=14\relax}% \def\retokenizingcatcodes{% %For rescanning previously scanned text, all true comments will already be gone, %but % signs may have been inserted by Cloze questions, so we want to treat them as 'other.' % % TODO: #'s are more worrisome. \normalcatcodes \catcode`\%=12\relax } % \end{macrocode} % % \subsection{Detokenization and Retokenization} % % We will be processing a \TeX\ token list. % Based on its content, sometimes we will want it to be detokenized to % individual characters, but other times we want it retokenized so that % \TeX's own parsing mechanism can gather up the parameters of macros. % We use the e\TeX\ primitive command |\scantokens| to do this. % % The following code (catcodes, groupings and all) defines a |\scantokens@to@macro| macro. % That will assemble and disassemble strings of tokens % using any changing schemes of catcodes we desire. % % We define |^^A| to be |\gdef\stm@saved|, while |^^B| and |^^C| are substitutes for |{| and |}|, respectively. % This permits us to define |\scantokens@to@macro| in a peculiar catcode regime. % \begin{macrocode} \begingroup \catcode`\^^A=13\gdef^^A{\gdef\stm@saved}% \catcode`\^^B=1\catcode`\^^C=2\relax \long\gdef\scantokens@to@macro#1#2#3{% % #1 = control sequence to be defined % #2 = command to change catcodes, e.g. \verbcatcodes, % and define any command sequences % #3 = text to be retokenized and saved into #1. \bgroup \def\texttorescan{#3}% \catcode`\^^A=13\catcode`\^^B=1\catcode`\^^C=2\relax \xa\def\xa\arg\xa{\xa^^A\xa^^B\texttorescan^^C}% #2% \catcode\endlinechar=9\relax% %\scantokens always sees an end-of-line character at its end and converts it to a space. %The catcode change sets \scantokens to ignore end-of-line chars. %In practice, we're always calling \scantokens on previously scanned text anyway, %so we won't miss any real end-of-line chars, since they were already converted to spaces. \xa\scantokens\xa{\arg}% \egroup \xa\def\xa#1\xa{\stm@saved}% }% \endgroup% \long\def\ultradetokenize@to@macro#1#2{% \scantokens@to@macro#1{\verbcatcodes}{#2}% }% \def\retokenizenormal@to@macro#1#2{% \scantokens@to@macro#1{\retokenizingcatcodes}{#2}% }% % \end{macrocode} % % \subsection{Level-Tracking} % 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. % \begin{macrocode} \newcount\moodle@mathmodedepth \moodle@mathmodedepth=0\relax \def\moodle@ifmathmode#1#2{% \ifnum\moodle@mathmodedepth>0\relax #1% \else #2% \fi } \newcount\grouplevel \newif\ifhtmlizer@active \htmlizer@activefalse % \end{macrocode} % % \subsection{Separation} % % This code separates a string of tokens into two parts. % Its parameters, |#1#2|, consist of tokenized text, % plus one terminal |\@htmlize@stop|. % We are trying to break up the text into its first group and the remainder. % This |\@htmlize@stop| is needed in case |#2| has the form ``|{...}|'', % since we don't want \TeX\ to strip the braces off. % Thus |\@htmlize@remainder| will definitely end in ``|\@htmlize@stop|''. % \begin{macrocode} \long\def\htmlize@grabblock#1#2\htmlize@rdelim@ii{% \long\def\htmlize@blockinbraces{#1}% \long\def\htmlize@remainder{#2}% }% % \end{macrocode} % % The next line defines the macro |\@htmlize@stop@detokenized| to contain % the string of tokens |\@htmlize@stop|, all of category code 12 (other) or 10 (letter). % We'll need this for comparison purposes later. % \begin{macrocode} \ultradetokenize@to@macro\@htmlize@stop@detokenized{\@htmlize@stop}% % \end{macrocode} % % The next line creates the macro |\htmlize@remove@stopcode|, % which removes the characters ``|\@htmlize@stop |'' from the end of a % detokenized sequence. % Its syntax when called is simply |\htmlize@remove@stopcode |\meta{material}, % with no delimiters, since the ``|\@htmlize@stop |'' is itself the delimiter. % \begin{macrocode} \xa\def\xa\htmlize@remove@stopcode\xa#\xa1\@htmlize@stop@detokenized{#1}% % \end{macrocode} % % \subsection{Main Code: the HTMLizer} % % \begin{macrocode} \newif\ifhtmlize@actioncs \newif\ifhtmlize@expandcs \newif\ifhtmlize@passcs \long\def\@@begin@cs{\begin}% \def\@@htmlize@stop{\@htmlize@stop}% \long\def\converttohtmlmacro#1#2{% \grouplevel=0\relax \def\htmlize@output{}% \htmlizer@activetrue% \converttohtml@int{#2}% \htmlizer@activefalse% \let#1=\htmlize@output\relax } \long\def\converttohtml@int#1{% \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 \egroup \advance\grouplevel by -1\relax }% \def\@lt{<}% \def\@gt{>}% \long\def\htmlize@recursive@i#1#2#3\htmlize@rdelim@i{% % #1#2#3 is a sequence of tokens. All should be categories 11 (letter) or 12 (other). % It terminates with the control sequences \@htmlize@stop\@htmlize@stop\@htmlize@stop. %\long\def\ds{(#1|#2|#3)}\show\ds \def\test@i{#1}% \def\test@ii{#2}% \ifx\test@i\@@htmlize@stop \let\htmlize@next@i=\relax \else \ifx\test@i\@otherlbrace% \xa\g@addto@macro\xa\htmlize@output\xa{\otherlbrace}% \bgroup \normalcatcodes %We need to rescan the input as TeX code, % so TeX can automatically pull off the first group in braces. % First, let's get rid of the terminal \@htmlize@stop codes. {\def\@htmlize@stop{}\xdef\htmlize@scrap{#1#2#3}}% \let\htmlize@text@to@rescan=\htmlize@scrap% % Next, we retokenize the code. \xa\retokenizenormal@to@macro\xa\htmlize@rescanned\xa{\htmlize@text@to@rescan}% % Now break it up into two pieces. \xa\htmlize@grabblock\htmlize@rescanned\@htmlize@stop\htmlize@rdelim@ii% % The first piece, \htmlize@blockinbraces, will be passed as a unit to \converttohtml@int. % The second part, \htmlize@remainder, will continue at this depth of grouping. % Therefore we'll detokenize \htmlize@remainder here. \xa\ultradetokenize@to@macro\xa\htmlize@remainder@detokenized\xa{\htmlize@remainder}% \edef\htmlize@remainder@detokenized{\xa\htmlize@remove@stopcode\htmlize@remainder@detokenized}% % % Now build \htmlize@next@i. % When done, should look like % \converttohtml@int{\htmlize@blockinbraces}% % \g@addto@macro\htmlize@output{\otherrbrace}% % \htmlize@recursive@i\htmlize@remainder@detokenized\@htmlize@stop\@htmlize@stop\@htmlize@stop\htmlize@rdelim@i% % but with all three arguments expanded. % Note that we are running \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}}% \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. % Now we get it out of this group. \egroup \let\htmlize@next@i=\htmlize@scrap \else \ifx\test@i\@otherdollar% % Math shift character. \ifx\test@ii\@otherdollar % Double dollar sign, so we're entering display math mode. % We grab everything between $$...$$, sanitize it, and add it verbatim to % our output. \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 % our output. \htmlize@inlinemathshift@replace#1#2#3\htmlize@rdelim@iii% \fi % 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, % \htmlize@remaining@text still includes the terminating \@htmlize@stop\@htmlize@stop\@htmlize@stop. \def\htmlize@next@i{\xa\htmlize@recursive@i\htmlize@remaining@text\htmlize@rdelim@i}% \else \ifx\test@i\@otherbackslash% % Control sequence. Oh boy. % There are three possible things to do: % 1. Retokenize everything, so we get a token list. % Expand this control sequence, the first one in the list, % to obtain a new token list. Then resume processing that list. % Examples: \def\emph#1{#1}, \def\rec#1{\frac{1}{#1}}, \def\inv{^{-1}} % \& --> & \# --> #; etc. % Environments: \begin{center}...\end{center} -->
    ...
    % 2. Retokenize everything, so we get a token list. % Let this first command (with its parameters) ACT. % This may involve work in TeX's stomach (e.g., with counters) % or with external files (e.g., image processing). % The command may directly add material to \htmlize@output, % but it should not typeset anything and should vanish from the % input stream when it is done. % When it's done, we somehow need to detokenize and resume % processing the remainder of the input stream. % Only commands explicitly crafted (or modified) to work % with moodle.sty can possibly do all this! % Examples: (modified) \includegraphics % Environments: \begin{clozemulti}, \begin{enumerate} % 3. Ignore that it's a command. Pass it right on as a character % sequence to \htmlize@output. % Examples: \alpha, \frac % Environments: \begin{array} % % #2 is only for items on a specific list. % #1 is anything that runs in TeX's mouth. % We could keep a list and give users a way to add to it. % I could also try expanding macros, using \ifcsmacro from etoolbox.sty % % The first step is to figure out what control sequence we're dealing with. % First, let's get rid of the terminal \@htmlize@stop codes. {\def\@htmlize@stop{}\xdef\htmlize@scrap{#1#2#3}}% \let\htmlize@text@to@rescan=\htmlize@scrap% % Next, we retokenize the code. \xa\retokenizenormal@to@macro\xa\htmlize@rescanned\xa{\htmlize@text@to@rescan}% % Now break it up into two pieces. \xa\htmlize@grabblock\htmlize@rescanned\@htmlize@stop\htmlize@rdelim@ii% \let\@htmlize@cs\htmlize@blockinbraces% \edef\htmlize@cs@string{\xa\string\@htmlize@cs}% % The first piece, \htmlize@blockinbraces, will contain the single token in \@htmlize@cs. % We'll need to keep the second part, \htmlize@remainder, since it probably % contains arguments to the cs in \@htmlize@cs. %\edef\ds{Encountered '\xa\string\@htmlize@cs'}\show\ds % % N.B. that \@htmlize@cs is a macro *containing* a single control sequence. % This is good for testing with \ifx. % \htmlize@cs@string contains the cs as a string, e.g., the characters "\emph". % \ifx\@htmlize@cs\@@begin@cs %This is a \begin. Begin environment-handling routine. % % Grab the first {...} from \htmlize@remainder, which is the argument % to \begin. \xa\htmlize@grabblock\htmlize@remainder\@htmlize@stop\htmlize@rdelim@ii% \let\htmlize@envname=\htmlize@blockinbraces% %We do not need the rest, so we won't pay any attention to the new %content of \htmlize@remainder. % %Now environments are non-expandable, %so there are only two possibilities: action or pass. \xa\ifinlist\xa{\htmlize@envname}{\htmlize@env@actionlist}% {% Action environment! %\bgroup %\def\ds{Encountered active environment \string\begin\{{\htmlize@envname}\}}\show\ds \def\htmlize@next@i{\xa\htmlize@do@actionenv\htmlize@rescanned\@htmlize@stop\htmlize@actionsequence@rdelim}% %The \bgroup is to active the environments. %The matching \egroup is found in \htmlize@do@actionenv. }{%An environment to pass to the HTML %We just pass the backslash from "\begin" and move on. \g@addto@macro\htmlize@output{#1}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% }% \else% %This is not an environment. Begin macro-handling routine. \htmlize@actioncsfalse \htmlize@expandcsfalse \htmlize@passcsfalse \xa\ifinlist\xa{\htmlize@cs@string}{\htmlize@cs@actionlist}% {%Action sequence! \htmlize@actioncstrue}% {% Not action sequence! \xa\ifinlist\xa{\htmlize@cs@string}{\htmlize@cs@expandlist}% {%CS to be expanded! \htmlize@expandcstrue% }% {%CS to be transcribed to XML \htmlize@passcstrue% }% }% %Now exactly one of \ifhtmlize@actioncs, \ifhtmlize@expandcs, and \ifhtmlize@passcs is true. \ifhtmlize@actioncs % It's an action-sequence. %\edef\ds{Must let \xa\string\@htmlize@cs\ act!}\show\ds %\show\htmlize@rescanned \def\htmlize@next@i{\xa\htmlize@do@actioncs\htmlize@rescanned\@htmlize@stop\htmlize@actionsequence@rdelim}% %\show\htmlize@rescanned % Note that \htmlize@do@actioncs should patch the command to have it % restart the scanning in time. \else \ifhtmlize@expandcs % control sequence to be expanded %\edef\ds{Must expand \xa\string\@htmlize@cs}\show\ds \bgroup \htmlize@redefine@expansionmacros %The \expandafters first expand \htmlize@rescanned, %and then expand its first token just once. \xa\xa\xa\gdef\xa\xa\xa\htmlize@scrap\xa\xa\xa{\htmlize@rescanned}% \egroup \xa\ultradetokenize@to@macro\xa\htmlize@remaining@text\xa{\htmlize@scrap}% \def\htmlize@next@i{\xa\htmlize@recursive@i\htmlize@remaining@text\@htmlize@stop\@htmlize@stop\@htmlize@stop\htmlize@rdelim@i}% \else % control sequence to be transcribed to XML. %\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 \else% \ifx\test@i\@othertilde% % The ~ becomes non-breaking space   \g@addto@macro\htmlize@output{\otherampersand nbsp;}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% \else \ifx\test@i\@lsinglequote% \ifx\test@ii\@lsinglequote% % Double left quote \g@addto@macro\htmlize@output{\otherampersand ldquo;}% \def\htmlize@next@i{\htmlize@recursive@i#3\htmlize@rdelim@i}% \else \g@addto@macro\htmlize@output{\otherampersand lsquo;}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% \fi \else \ifx\test@i\@rsinglequote% \ifx\test@ii\@rsinglequote% %Double right quote \g@addto@macro\htmlize@output{\otherampersand rdquo;}% \def\htmlize@next@i{\htmlize@recursive@i#3\htmlize@rdelim@i}% \else \g@addto@macro\htmlize@output{\otherampersand rsquo;}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% \fi \else \ifx\test@i\@doublequote \g@addto@macro\htmlize@output{\otherampersand rdquo;}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% \else \ifx\test@i\@lt \moodle@ifmathmode{\g@addto@macro\htmlize@output{\otherampersand lt;}}% {\g@addto@macro\htmlize@output{<}}% \def\htmlize@next@i{\htmlize@recursive@i#2#3\htmlize@rdelim@i}% \else \ifx\test@i\@gt \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 % 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 \htmlize@next@i }% \def\@lsinglequote{`}% \def\@rsinglequote{'}% \def\@doublequote{"}% % \end{macrocode} % % \subsection{Math Mode handling} % % In the following, note that the |\|\meta{*}|mathrightdelim|'s gobble an argument. % This is so ``|$a$ is...|" can turn into % ``\ldots |a\|\meta{*}|mathrightdelim{} is...|" % and preserve a trailing space. % \begin{macrocode} \edef\inlinemathleftdelim{\otherbackslash(}% \def\inlinemathrightdelim#1{\advancemathmodecounter{-1}% \g@addto@macro\htmlize@output{\otherbackslash)}}% \edef\displaymathleftdelim{
    \otherbackslash[}% \def\displaymathrightdelim#1{\advancemathmodecounter{-1}% \g@addto@macro\htmlize@output{\otherbackslash]
    }}% {\catcode`\$=12\relax% \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}% \xdef\htmlize@remaining@text{\expandonce\mathtext% \otherbackslash inlinemathrightdelim{}% \expandonce\aftertext}% %\show\htmlize@remaining@text }% \gdef\htmlize@displaymathshift@replace$$#1$$#2\htmlize@rdelim@iii{% \xa\g@addto@macro\xa\htmlize@output\xa{\displaymathleftdelim}% \advancemathmodecounter{1}% \def\mathtext{#1}% \def\aftertext{#2}% \xdef\htmlize@remaining@text{\expandonce\mathtext% \otherbackslash displaymathrightdelim{}% \expandonce\aftertext}% }% } % \end{macrocode} % % \subsection{Engines for Control Sequences} % % There are three kinds of control sequences that need special handling: % \begin{enumerate} % \item Action environments % \item Action command sequences % \item Expansion macros % \end{enumerate} % % \subsubsection{Engine for running action environments} % \begin{macrocode} \long\def\htmlize@do@actionenv#1#2\@htmlize@stop\htmlize@actionsequence@rdelim{% \bgroup %The corresponding \egroup is given in \htmlize@proceedwiththerest, %to localize the changes to the environment definitions. \htmlize@activate@environments \gdef\htmlize@afteraction@hook{}% #1#2\@htmlize@stop\htmlize@actionsequence@rdelim% } \def\htmlize@patchendenvironment{\swaptotrueendenvironment{\xa\htmlize@proceedwiththerest\htmlize@afteraction@hook}}% \def\swaptotrueendenvironment#1#2\if@ignore\@ignorefalse\ignorespaces\fi{#2\if@ignore\@ignorefalse\ignorespaces\fi#1}% \long\def\htmlize@record@environment#1{% \listadd{\htmlize@env@actionlist}{#1}% } \long\def\html@newenvironment#1#2{% \listadd{\htmlize@env@actionlist}{#1}% \g@addto@macro\htmlize@activate@environments{% \xa\let\csname #1\endcsname\relax% \xa\let\csname end#1\endcsname\relax% \NewEnviron{#1}{% #2% }[\htmlize@patchendenvironment]% }% } \def\htmlize@activate@environments{}% % \end{macrocode} % % \subsubsection{Engine for running action command sequences} % % The following automatically adds the ``engine'' to do the command % and then resume processing the \LaTeX\ into HTML. % It uses the |xpatch| package, which says it works with anything % defined using |\newcommand| etc. and |\newenvironment| etc. % \begin{macrocode} \gdef\htmlize@afteraction@hook{}% \long\def\htmlize@do@actioncs#1#2\htmlize@actionsequence@rdelim{% % #1#2 contains the current string to be rendered into HTML; % N.B. it has been tokenized at this point, % so TeX can process it directly. % #1 = the command sequence we need to execute % #2 = the rest of the string % % First, we patch the desired command so that, when it is over, % it calls \htmlize@proceedwiththerest. % We do this within the group, so as not to permanently change the command. \bgroup % The matching \egroup is issued in \htmlize@proceedwiththerest, % so that the changes made by \htmlize@activate@css are localized to just the command itself. \gdef\htmlize@afteraction@hook{}% \htmlize@activate@css% \def\test@i{#1}% \ifx\test@i\@relax \def#1{\xa\htmlize@proceedwiththerest\htmlize@afteraction@hook}% \else \xapptocmd#1{\xa\htmlize@proceedwiththerest\htmlize@afteraction@hook}{}{\PackageError{Could not patch the command \string#1!}}% \fi % Now we call that patched command. #1#2\htmlize@actionsequence@rdelim% %The matching \egroup now is built into the command #1. } \long\def\htmlize@proceedwiththerest#1\htmlize@actionsequence@rdelim{% % The action cs has done its work. % Now we gather up the remaining tokens, detokenize them, % remove the \@htmlize@stop, and get back to work transcribing it. \egroup %This \egroup matches the \bgroup that was issued either in \htmlize@do@actioncs or in \htmlize@do@actionenv \ultradetokenize@to@macro\htmlize@remainder@detokenized{#1}% %This will contain an extra \@htmlize@stop, so we remove it. \xa\xa\xa\def\xa\xa\xa\htmlize@remainder@detokenized\xa\xa\xa{\xa\htmlize@remove@stopcode\htmlize@remainder@detokenized}% %Now we get back to work transcribing the remainder. \xa\htmlize@recursive@i\htmlize@remainder@detokenized\@htmlize@stop\@htmlize@stop\@htmlize@stop\htmlize@rdelim@i% } \long\def\htmlize@record@action#1{% \xa\listadd\xa\htmlize@cs@actionlist\xa{\string#1}% } \def\htmlize@activate@css{}% \def\html@action@def#1{% \htmlize@record@action{#1}% \xa\def\xa\htmlize@scrap\xa{\xa\let\xa#1\csname html@\string#1\endcsname}% \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. } % \end{macrocode} % % \subsubsection{Engine for expansion control sequences} % % Calling |\htmlize@redefine@expansionmacros| will redefine % the macros for us. It starts out empty. % \begin{macrocode} \def\htmlize@redefine@expansionmacros{}% % \end{macrocode} % If |\mymacro| needs no changes to be suited for expansion, % you can simply call |\htmlize@record@expand{\mymacro}| % or |\htmlregister{\mymacro}| % to record that it should be expanded on its way to the HTML. % Examples would be user-built macros such as |\inv|$\to$|^{-1}| % or |\N|$\to$|\mathbb{N}|. % \begin{macrocode} \long\def\htmlize@record@expand#1{% \xa\listadd\xa\htmlize@cs@expandlist\xa{\string#1}% } \let\htmlregister=\htmlize@record@expand % \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...| % Parameters are okay. % An example would be % |\html@def\emph#1{#1}|. % \begin{macrocode} \long\def\html@def#1{% \htmlize@record@expand{#1}% \xa\def\xa\htmlize@scrap\xa{\xa\let\xa#1\csname html@\string#1\endcsname}% \xa\g@addto@macro\xa\htmlize@redefine@expansionmacros\xa{\htmlize@scrap}% \xa\def\csname html@\string#1\endcsname% } % \end{macrocode} % Note that when |\html@def| expands out, it ends with |\def\html@\oldcsname| % which abuts directly on the remainder of the definition. % % \subsection{Specific Control Sequences for Action and Expansion} % % Now that we have that machinery in place, % we define specific environments, action control sequences, and macros to % expand to accomplish our purposes. % % \subsubsection{Action Environments} % \begin{macrocode} \htmlize@record@environment{clozemulti} \htmlize@record@environment{multi} \htmlize@record@environment{clozenumerical} \htmlize@record@environment{numerical} \htmlize@record@environment{clozeshortanswer} \htmlize@record@environment{shortanswer} \html@newenvironment{center}{\xdef\htmlize@afteraction@hook{
    \expandonce\BODY
    }}% \html@newenvironment{enumerate}{% \xa\gatheritems\xa{\BODY}% \gdef\htmlize@afteraction@hook{
      }% \loopthroughitemswithcommand{\moodle@itemtoLI}% \g@addto@macro\htmlize@afteraction@hook{
    }% }% \html@newenvironment{itemize}{% \xa\gatheritems\xa{\BODY}% \gdef\htmlize@afteraction@hook{
      }% \loopthroughitemswithcommand{\moodle@itemtoLI}% \g@addto@macro\htmlize@afteraction@hook{
    }% }% \def\moodle@itemtoLI#1{% \g@addto@macro\htmlize@afteraction@hook{
  • #1
  • }% }% % \end{macrocode} % % \subsubsection{Action Control Sequences} % % \begin{macrocode} \def\advancemathmodecounter#1{% \global\advance\moodle@mathmodedepth by #1\relax } \htmlize@record@action{\advancemathmodecounter}% \htmlize@record@action{\relax}% \html@action@def\#{\g@addto@macro\htmlize@output{\otherhash}}% \html@action@def\&{\g@addto@macro\htmlize@output{\otherampersand}}% \html@action@def\\{\moodle@ifmathmode{\g@addto@macro\htmlize@output{\otherbackslash\otherbackslash}}% {\g@addto@macro\htmlize@output{
    }}}% \html@action@def\{{% \moodle@ifmathmode{\g@addto@macro\htmlize@output{\otherbackslash\otherlbrace}}% {\g@addto@macro\htmlize@output{\otherlbrace}}% }% \html@action@def\}{% \moodle@ifmathmode{\g@addto@macro\htmlize@output{\otherbackslash\otherrbrace}}% {\g@addto@macro\htmlize@output{\otherrbrace}}% }% \html@action@def\ldots{% \moodle@ifmathmode{\g@addto@macro\htmlize@output{\string\ldots}}% {\g@addto@macro\htmlize@output{...}}% }% \html@action@def\dots{% \moodle@ifmathmode{\g@addto@macro\htmlize@output{\string\dots}}% {\g@addto@macro\htmlize@output{...}}% }% \html@action@def\ {% \moodle@ifmathmode{\g@addto@macro\htmlize@output{\otherbackslash\otherspace}}% {\g@addto@macro\htmlize@output{\otherspace}}% }% \html@action@def\${% \g@addto@macro\htmlize@output{\otherdollar}% }% \html@action@def\clozetilde{% \xa\g@addto@macro\xa\htmlize@output\xa{\othertilde}% }% %Diacritical marks over vowels {\catcode`|=3\relax \gdef\htmlize@vowels{a|e|i|o|u|A|E|I|O|U|}} \def\htmlize@define@diacritic#1#2{% \htmlize@record@action{#1}% \g@addto@macro\htmlize@activate@css{% \def#1##1{% \ifinlist{##1}{\htmlize@vowels}% {\g@addto@macro\htmlize@output{\otherampersand##1#2;}}% {\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}% %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;}% \else \ifx\test@i\@o \def\toadd{\otherampersand\otherhash337;}% \else \ifx\test@i\U \def\toadd{\otherampersand\otherhash368;}% \else \ifx\test@i\u \def\toadd{\otherampersand\otherhash369;}% \else \def\toadd{\otherbackslash\otherlbrace#1\otherrbrace}% \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;}}% {\xa\g@addto@macro\htmlize@output\xa{\string\~#1}}% }% % \end{macrocode} % % \subsubsection{Command sequences for Expansion} % % \begin{macrocode} \html@def\underline#1{#1} \html@def\emph#1{#1}% \html@def\textbf#1{#1}% \html@def\blank{____________}% \html@def\par{

    }% \html@def\aa{\å}% \html@def\AA{\Å}% \html@def\ae{\æ}% \html@def\AE{\Æ}% \html@def\S{\§}% \html@def\euro{\€}% \html@def\texteuro{\€}% \html@def\o{\ø}% \html@def\O{\Ø}% \html@def\ss{\ß}% \html@def\l{\ł}% \html@def\L{\Ł}% % AH CUSTOM MACROS TO EXPAND --- remove these before publishing! \htmlize@record@expand{\inv}% \htmlize@record@expand{\rec}% \htmlize@record@action\inlinemathrightdelim \htmlize@record@action\displaymathrightdelim % \end{macrocode} % % \subsection{Graphics via {\tt\string\includegraphics}} % % \subsubsection{External program command lines} % We first set up commands for the external programs. % \begin{macrocode} \def\htmlize@setexecutable#1{% % Defines macro #1 to be #2 in a verbatim mode suitable for filenames \def\htmlize@executable@macro{#1}% \bgroup\catcode`\|=0\catcode`\\=12\relax% \htmlize@setexecutable@int } \def\htmlize@setexecutable@int#1{% \egroup \expandafter\def\htmlize@executable@macro{#1}% } \def\ghostscriptcommand{\htmlize@setexecutable\gs} \def\opensslcommand{\htmlize@setexecutable\openssl} \def\imagemagickcommand{\htmlize@setexecutable\htmlize@imagemagick@convert} \ifwindows% \ghostscriptcommand{gswin64c.exe}% \else% \ghostscriptcommand{gs}% \fi% \opensslcommand{openssl}% \imagemagickcommand{convert}% % \end{macrocode} % % \subsubsection{Graphics key-handling} % Next, we get ready to handle keys like |height=4cm| or |width=3cm| or |ppi=72|. % \begin{macrocode} \define@cmdkeys{moodle@includegraphics}[moodle@graphics@]{ppi} \define@cmdkey{moodle}[moodle@graphics@]{ppi}{}% This is so the ppi key can be set at the document, quiz, or question level. \define@cmdkeys{Gin}{ppi}% This is so the original \includegraphics will not object to a key of ppi. \setkeys{moodle@includegraphics}{ppi=103} \newdimen\moodle@graphics@temp@dimen \newcount\moodle@graphics@height@pixels \newcount\moodle@graphics@width@pixels \def\moodle@graphics@dimentopixels#1#2{% \moodle@graphics@temp@dimen=#2\relax \moodle@graphics@temp@dimen=0.013837\moodle@graphics@temp@dimen \xa\moodle@graphics@temp@dimen\xa=\moodle@graphics@ppi\moodle@graphics@temp@dimen #1=\moodle@graphics@temp@dimen \divide #1 by 65536\relax } \define@key{moodle@includegraphics}{height}[]{% \moodle@graphics@dimentopixels{\moodle@graphics@height@pixels}{#1}% } \define@key{moodle@includegraphics}{width}[]{% \moodle@graphics@dimentopixels{\moodle@graphics@width@pixels}{#1}% } \setkeys{moodle@includegraphics}{height=0pt,width=0pt} % \end{macrocode} % % \subsubsection{Graphics conversion to HTML} % \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][]{% \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 % pixels? % TO DO: What about \includegraphics[scale=0.7] ? % Other keys: keepaspectratio=true|false, angle (rotation), clip & trim \ifnum\moodle@graphics@height@pixels=0\relax \ifnum\moodle@graphics@width@pixels=0\relax % No size specified. Default to height of 200 pixels. \def\moodle@graphics@geometry{x200}% \def\moodle@graphics@htmlgeometry{}% \else % Width only specified. \edef\moodle@graphics@geometry{\number\moodle@graphics@width@pixels}% \edef\moodle@graphics@htmlgeometry{width=\number\moodle@graphics@width@pixels}% \fi \else \ifnum\moodle@graphics@width@pixels=0\relax % Height only specified. The `x' is part of the syntax. \edef\moodle@graphics@geometry{x\number\moodle@graphics@height@pixels}% \edef\moodle@graphics@htmlgeometry{height=\number\moodle@graphics@height@pixels}% \else % Height and width both specified. The `!' is part of the syntax. \edef\moodle@graphics@geometry{\number\moodle@graphics@width@pixels x\number\moodle@graphics@height@pixels!}% \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}% %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 \savebaselxivdata@recursive \closein\baseLXIVdatafile \xa\global\xa\let\csname picbaselxiv@graphics@#2\endcsname=\moodle@newpic@baselxiv% \edef\htmlize@imagetag{}% \xa\g@addto@macro\xa\htmlize@output\xa{\htmlize@imagetag}% \message{moodle.sty: tag inserted.^^J}% \egroup % Now we re-insert the code to get the HTMLizing going again. \xa\htmlize@proceedwiththerest\htmlize@afteraction@hook } % \end{macrocode} % The following code accomplishes the reading of an |.enc| file into memory. % It is also used by the \TikZ\ code below. % \begin{macrocode} \newread\baseLXIVdatafile \def\savebaselxivdata@recursive{% \ifeof\baseLXIVdatafile \let\baselxiv@next=\relax \else \read\baseLXIVdatafile to \datalinein %\message{<<\datalinein>>^^J} \ifx\datalinein\@moodle@par \let\baselxiv@next=\relax \else %We add tokens manually, rather than with \g@addto@macro, to save time. \xa\xa\xa\gdef\xa\xa\xa\moodle@newpic@baselxiv\xa\xa\xa{\xa\moodle@newpic@baselxiv\datalinein^^J}% \let\baselxiv@next=\savebaselxivdata@recursive \fi \fi \baselxiv@next } % \end{macrocode} % % \subsection{\TikZ\ Picture Handling} % If the user is not using the \TikZ\ package, there is no need to waste time % loading it. Without \TikZ\ loaded, however, many of the following commands % are undefined. % Our solution is to wait until |\AtBeginDocument| and then test whether % \TikZ\ is loaded. If so, we make the appropriate definitions. % \begin{macrocode} \newif\ifmoodle@tikzloaded \moodle@tikzloadedfalse \AtBeginDocument{ \ifx\tikzpicture\@undefined \moodle@tikzloadedfalse \else \moodle@tikzloadedtrue \fi \ifmoodle@tikzloaded \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}}% \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}}% \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 %\jobnamewithsuffixtomacro{\htmlize@picbasename}{-tikztemp-\the\numconvertedpictures}% %\xa\tikzsetnextfilename\xa{\htmlize@picbasename}% \tikzsetnextfilename{\tikzexternalrealjob-tikztemp-\the\numconvertedpictures}% \moodle@oldtikzpicture% }{}% % 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 } \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 \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}} % % The HTMLizer version of the tikzpicture environment, % which writes an tag to the XML file. \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{}% \xa\g@addto@macro\xa\htmlize@output\xa{\htmlize@imagetag}% }[\htmlize@patchendenvironment]% }% \else %TikZ not loaded. Provide dummy definitions for commands. \long\def\tikzifexternalizing#1#2{#2}% \fi } \newcount\numconvertedpictures \numconvertedpictures=0\relax \newcount\numpicturesread \numpicturesread=0\relax \def\savebaselxivdata{% \def\moodle@newpic@baselxiv{}% \openin\baseLXIVdatafile=\tikzexternalrealjob-tikztemp-\the\numconvertedpictures.enc\relax \savebaselxivdata@recursive \closein\baseLXIVdatafile \xa\global\xa\let\csname picbaselxiv@\the\numconvertedpictures\endcsname=\moodle@newpic@baselxiv% } \def\@moodle@par{\par}% % \end{macrocode} % Finally, we clean up our mess by deleting the temporary PDF, PNG, and ENC % files we created. % \begin{macrocode} \AtEndDocument{% \ifmoodle@tikzloaded \@moodle@ifgeneratexml{% \ifwindows \immediate\write18{del \tikzexternalrealjob-tikztemp-*.*}% \else \immediate\write18{rm \tikzexternalrealjob-tikztemp-*.*}% \fi }{}% \fi } % TO DO: % * sizing options for TikZ pictures? % * In cloze multi, how to handle HTML or TeX in answers? In particular, what about ~? % \end{macrocode} % % \Finale \endinput