mathmaker
0.6(alpha)
|
00001 # -*- coding: utf-8 -*- 00002 00003 # Mathmaker creates automatically maths exercises sheets 00004 # with their answers 00005 # Copyright 2006-2014 Nicolas Hainaux <nico_h@users.sourceforge.net> 00006 00007 # This file is part of Mathmaker. 00008 00009 # Mathmaker is free software; you can redistribute it and/or modify 00010 # it under the terms of the GNU General Public License as published by 00011 # the Free Software Foundation; either version 3 of the License, or 00012 # any later version. 00013 00014 # Mathmaker is distributed in the hope that it will be useful, 00015 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 # GNU General Public License for more details. 00018 00019 # You should have received a copy of the GNU General Public License 00020 # along with Mathmaker; if not, write to the Free Software 00021 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 00022 00023 import math 00024 import sys 00025 from lib.common import latex 00026 from lib.common import cfg 00027 from lib.common import software 00028 from lib.common.cst import * 00029 00030 from core.base import * 00031 from core.base_calculus import * 00032 from lib import * 00033 import core.base_calculus 00034 00035 import Structure 00036 00037 00038 # ------------------------------------------------------------------------------ 00039 # -------------------------------------------------------------------------- 00040 # ------------------------------------------------------------------------------ 00041 ## 00042 # @class LaTeX 00043 # @brief This machine knows how to write LaTeX commands & math expressions 00044 # @todo When creating another machine, some things might have to change here 00045 class LaTeX(Structure.Structure): 00046 00047 00048 00049 00050 00051 # -------------------------------------------------------------------------- 00052 ## 00053 # @brief Constructor 00054 # The created machine is set to the beginning of an expression, 00055 # its language is the default one (from cfg file or in case of any 00056 # problem, from text.DEFAULT_LANGUAGE) 00057 # its encoding is set to the default one (from cfg file or in case of any 00058 # problem, from latex.DEFAULT_ENCODING) 00059 # @param expression_begins True if machine's at an expression's beginning 00060 # @param **options Any options 00061 # @return One instance of machine.LaTeX 00062 # @todo The warning handling should be done in the main program 00063 def __init__(self, language, **options): 00064 00065 self.text_sizes = latex.TEXT_SIZES 00066 00067 self.font_size_offset = 0 00068 00069 self.create_pic_files = True 00070 00071 if 'create_pic_files' in options \ 00072 and not options['create_pic_files'] in YES: 00073 #___ 00074 self.create_pic_files = False 00075 00076 # Encoding... 00077 self.encoding = latex.DEFAULT_ENCODING 00078 try: 00079 encoding_cfg = cfg.get_value_from_file(latex.FORMAT, "ENCODING") 00080 except error.UnreachableData: 00081 pass 00082 else: 00083 self.encoding = encoding_cfg 00084 00085 # Language... 00086 self.language = "" 00087 00088 00089 if not language in latex.LANGUAGE_PACKAGE_NAME: 00090 error.write_warning(_("the LaTeX language package matching the \ 00091 chosen language (" + language + ") is not implemented yet in %(software_ref)s, \ 00092 which will try to use the language entry from the configuration file instead") \ 00093 % {'software_ref':software.NAME}) 00094 if not default.LANGUAGE in latex.LANGUAGE_PACKAGE_NAME: 00095 error.write_warning(_("the LaTeX language package matching \ 00096 the language entry from the configuration file is neither implemented in \ 00097 %(software_ref)s, which will use the english package instead")) 00098 self.language = latex.ENGLISH 00099 else: 00100 self.language = latex.LANGUAGE_PACKAGE_NAME[default.LANGUAGE] 00101 00102 self.language_code = latex.LANGUAGE_CODE_NAMES[self.language] 00103 00104 else: 00105 self.language = latex.LANGUAGE_PACKAGE_NAME[language] 00106 self.language_code = language 00107 00108 00109 self.markup = latex.MARKUP 00110 00111 self.out = sys.stdout 00112 00113 if 'out' in options: 00114 self.out = options['out'] 00115 00116 self.redirect_output_to_str = False 00117 00118 00119 00120 00121 00122 00123 00124 # -------------------------------------------------------------------------- 00125 ## 00126 # @brief Write the complete LaTeX header of the sheet to the output. 00127 def write_document_header(self): 00128 result = "" 00129 00130 result += "% " + _( \ 00131 "%(document_format)s document generated by %(software_ref)s") \ 00132 % {'document_format':latex.FORMAT_NAME_PRINT, 00133 'software_ref':software.NAME_PRINTABLE + " " \ 00134 + software.VERSION} \ 00135 + "\n" 00136 result += "% " \ 00137 + _("%(software_ref)s is free software. Its license \ 00138 is %(software_license)s.") % {'software_ref' : software.NAME_PRINTABLE, 00139 'software_license' : software.LICENSE} \ 00140 + "\n" 00141 result += "% " \ 00142 + _("Further details on %(software_website)s") \ 00143 % {'software_website' : software.WEBSITE} \ 00144 + "\n" 00145 result += "% " + software.COPYRIGHT + " " + software.AUTHOR +"\n" 00146 00147 result += "\documentclass[a4paper,fleqn,12pt]{article}" + "\n" 00148 if self.encoding == latex.UCS_UTF8X: 00149 result += r"\usepackage{ucs}" + "\n" 00150 result += r"\usepackage[" + latex.UTF8X + "]{inputenc}" \ 00151 + "\n" 00152 else: 00153 result += r"\usepackage[" + self.encoding + "]{inputenc}" \ 00154 + "\n" 00155 00156 result += r"\usepackage[" \ 00157 + self.language \ 00158 + "]{babel}" + "\n" 00159 00160 result += r"\usepackage[T1]{fontenc}" + "\n" 00161 result += "% " + _("To solve accent problems : ") + "\n" 00162 result += r"%\usepackage{lmodern}" + "\n" 00163 result += "% " + _("Using lmodern package might be better than \ 00164 cm aeguill") + "\n" 00165 result += r"\usepackage[cm]{aeguill}" + "\n" 00166 result += "% " + _("To strike out numbers ") + "\n" 00167 result += r"\usepackage{cancel}" + "\n" 00168 result += "% " + _("To use the margin definition command") + "\n" 00169 result += r"\usepackage{geometry}" + "\n" 00170 result += "% " + _("To use the commands from %(theorem)s ") \ 00171 % {'theorem' : '"theorem"'} + "\n" 00172 result += r"%\usepackage{theorem}" + "\n" 00173 result += "% " + _("To use multicol environment") + "\n" 00174 result += r"\usepackage{multicol}" + "\n" 00175 result += "% " + _("To use extra commands to handle tabulars") + "\n" 00176 result += r"\usepackage{array}" + "\n" 00177 result += "% " + _("For pretty underlining") + "\n" 00178 result += r"\usepackage{ulem}" + "\n" 00179 result += "% " + _("To include .eps pictures") + "\n" 00180 result += r"\usepackage{graphicx}" + "\n" 00181 result += "% " + _("To use other mathematical symbols") + "\n" 00182 result += r"\usepackage{amssymb}" + "\n" 00183 result += r"\usepackage{amsmath}" + "\n" 00184 result += "% " + _("To use the euro symbol") + "\n" 00185 result += r"\usepackage{eurosym}" + "\n" 00186 result += "% " + _("To draw") + "\n" 00187 result += r"\usepackage{tikz}" + "\n" 00188 result += "% " + _("Page layout ") + "\n" 00189 result += "\geometry{hmargin=1.5cm, vmargin=1.5cm}" + "\n" 00190 result += "\setlength{\parindent}{0cm}" + "\n" 00191 result += "\pagestyle{empty}" + "\n" 00192 result += " " + "\n" 00193 result += "%%% " + _("If you wish to include a picture, \ 00194 please use this command :") + "\n" 00195 result += "%%% \includegraphics[height=6cm]{" 00196 result += _("file_name") 00197 result += ".eps} \n" 00198 result += " " + "\n" 00199 result += "% " + _("Exercises counter") + "\n" 00200 result += "\\newcounter{n}" + "\n" 00201 result += "% " + _("Definition of the %(exercise)s command, \ 00202 which will insert the word %(Exercise)s in bold, with its number and \ 00203 automatically increments the counter") \ 00204 % {'exercise' : '"exercise"', 00205 'Exercise' : _("Exercise")} \ 00206 + "\n" 00207 result += "\\newcommand{\exercise}{\\noindent \hspace{-.25cm}" \ 00208 + " \stepcounter{n} " + self.translate_font_size('Large') \ 00209 + " \\textbf{" \ 00210 + _("Exercise") \ 00211 + " \\arabic{n}} " \ 00212 + "\\newline " + self.translate_font_size('large') + " }" + " \n" 00213 result += "% " + _("Definition of the command resetting the \ 00214 exercises counter (which is useful when begining to write the answers sheet)")\ 00215 + "\n" 00216 result += "\\newcommand{\\razcompteur}{\setcounter{n}{0}}" + "\n" 00217 result += " " + "\n" 00218 result += r"\usetikzlibrary{calc}" + "\n" 00219 00220 if self.redirect_output_to_str: 00221 return result 00222 00223 else: 00224 self.out.write(result) 00225 00226 00227 00228 00229 00230 # -------------------------------------------------------------------------- 00231 ## 00232 # @brief Writes to the output the command to begin the document 00233 def write_document_begins(self): 00234 output_str = "\\begin{document}" + "\n" 00235 if self.redirect_output_to_str: 00236 return output_str 00237 else: 00238 self.out.write(output_str) 00239 00240 ## 00241 # @brief Writes to the output the end of document command 00242 def write_document_ends(self): 00243 output_str = "\end{document} " + "\n" 00244 if self.redirect_output_to_str: 00245 return output_str 00246 else: 00247 self.out.write(output_str) 00248 00249 ## 00250 # @brief Writes to the output the command displaying an exercise's title 00251 def write_exercise_number(self): 00252 output_str = "\exercise" + "\n" 00253 if self.redirect_output_to_str: 00254 return output_str 00255 else: 00256 self.out.write(output_str) 00257 00258 ## 00259 # @brief Writes to the output the jump to next page command 00260 def write_jump_to_next_page(self): 00261 output_str = "\\newpage" + "\n" 00262 if self.redirect_output_to_str: 00263 return output_str 00264 else: 00265 self.out.write(output_str) 00266 00267 ## 00268 # @brief Writes to the output the exercises counter reinitialize command 00269 def reset_exercises_counter(self): 00270 output_str = "\\razcompteur " + "\n" 00271 if self.redirect_output_to_str: 00272 return output_str 00273 else: 00274 self.out.write(output_str) 00275 00276 ## 00277 # @brief Writes to the output the new line command 00278 def write_new_line(self, **options): 00279 output_str = "\\newline " + "\n" 00280 if 'check' in options: 00281 if options['check'] == '\]': 00282 output_str = "" 00283 if self.redirect_output_to_str: 00284 return output_str 00285 else: 00286 self.out.write(output_str) 00287 00288 ## 00289 # @brief Writes to the output two commands writing two new lines 00290 def write_new_line_twice(self, **options): 00291 output_str = "\\newline " + "\n" + " \\newline " + "\n" 00292 if 'check' in options: 00293 if options['check'] == '\]': 00294 output_str = "" 00295 if self.redirect_output_to_str: 00296 return output_str 00297 else: 00298 self.out.write(output_str) 00299 00300 ## 00301 # @brief Prints the given string as a mathematical expression 00302 def write_math_style2(self, given_string): 00303 output_str = self.markup['opening_math_style2'] + " " \ 00304 + given_string \ 00305 + " " + self.markup['closing_math_style2'] 00306 00307 if self.redirect_output_to_str: 00308 return output_str 00309 else: 00310 self.out.write(output_str) 00311 00312 ## 00313 # @brief Prints the given string as a mathematical expression 00314 def write_math_style1(self, given_string): 00315 output_str = self.markup['opening_math_style1'] + " " \ 00316 + given_string \ 00317 + " " + self.markup['closing_math_style1'] 00318 00319 if self.redirect_output_to_str: 00320 return output_str 00321 else: 00322 self.out.write(output_str) 00323 00324 ## 00325 # @brief Writes to the output the given string 00326 # @option emphasize='bold'|'italics'|'underlined' 00327 def write(self, given_string, **options): 00328 output_str = "" 00329 00330 if 'emphasize' in options: 00331 if options['emphasize'] == 'bold': 00332 output_str = "\\textbf{" + given_string + "}" + "\n" 00333 elif options['emphasize'] == 'italics': 00334 output_str = "\\textit{" + given_string + "}" + "\n" 00335 elif options['emphasize'] == 'underlined': 00336 output_str = r"\uline{" + given_string + "}" + "\n" 00337 else: 00338 output_str = given_string 00339 else: 00340 output_str = given_string 00341 00342 if 'multicolumns' in options: 00343 if type(options['multicolumns']) == int \ 00344 and options['multicolumns'] >= 1: 00345 #___ 00346 output_str = "\\begin{multicols}{" \ 00347 + str(options['multicolumns']) + "} " + "\n" \ 00348 + output_str \ 00349 + "\end{multicols}" + "\n" 00350 else: 00351 raise error.OutOfRangeArgument(options['multicolumns'], 00352 ' should be an int >=1\n') 00353 00354 if self.redirect_output_to_str: 00355 return output_str 00356 else: 00357 self.out.write(output_str) 00358 00359 00360 ## 00361 # @brief turn the size keyword in LaTeX matching keyword 00362 # @warning if you chose a too low or too high value as font_size_offset, 00363 # @warning then all the text will be either tiny or Huge. 00364 def translate_font_size(self, arg): 00365 if not type(arg) == str: 00366 raise error.UncompatibleType(objct, "String") 00367 elif not arg in TEXT_SCALES: 00368 raise error.UncompatibleType(objct, "a text size" \ 00369 + " (see TEXT_SCALES)") 00370 00371 arg_num = TEXT_RANKS[arg] 00372 00373 size_to_use = self.font_size_offset + arg_num 00374 00375 if size_to_use < 0: 00376 size_to_use = 0 00377 elif size_to_use > len(self.text_sizes) - 1: 00378 size_to_use = len(self.text_sizes) - 1 00379 00380 return self.text_sizes[size_to_use] 00381 00382 00383 ## 00384 # @brief Writes to the output the command setting the text size 00385 def write_set_font_size_to(self, arg): 00386 output_str = self.translate_font_size(arg) + "\n" 00387 if self.redirect_output_to_str: 00388 return output_str 00389 else: 00390 self.out.write(output_str) 00391 00392 00393 00394 00395 ## 00396 # @brief Writes a table filled with the given [strings] 00397 # @param size : (nb of lines, nb of columns) 00398 # @param col_widths : [int] 00399 # @param content : [strings] 00400 # @options : borders='all' 00401 # @options : unit='inch' etc. (check the possibilities...) 00402 # def write_table(self, size, col_widths, content, **options): 00403 # n_col = size[1] 00404 # n_lin = size[0] 00405 # result = "" 00406 00407 # length_unit = 'cm' 00408 # if 'unit' in options: 00409 # length_unit = options['unit'] 00410 # 00411 # tabular_format = "" 00412 # v_border = "" 00413 # h_border = "" 00414 ## center = "" 00415 # new_line_sep = "\\\\" + "\n" 00416 # 00417 # if 'center' in options: 00418 # center = ">{\centering}" 00419 # new_line_sep = "\\tabularnewline" + "\n" 00420 # 00421 # if 'borders' in options and options['borders'] == 'all': 00422 # v_border = "|" 00423 # h_border = "\\hline \n" 00424 00425 # for i in xrange(len(col_widths)): 00426 # tabular_format += v_border \ 00427 # + center \ 00428 # + "p{" + str(col_widths[i]) + " " \ 00429 # + str(length_unit) + "}" 00430 # 00431 # tabular_format += v_border 00432 00433 # result += "\\begin{tabular}{"+ tabular_format + "}" + "\n" 00434 # result += h_border 00435 00436 # # for i in xrange(n_lin): 00437 # for j in xrange(n_col): 00438 # result += str(content[i*n_col + j]) 00439 # if j != n_col - 1: 00440 # result += "&" + "\n" 00441 # if i != n_lin - 1: 00442 # result += new_line_sep + h_border 00443 00444 # result += new_line_sep + h_border 00445 # result += "\end{tabular} " + "\n" 00446 00447 # if self.redirect_output_to_str: 00448 # return result 00449 # else: 00450 # self.out.write(result) 00451 00452 00453 ## 00454 # @brief Writes content arranged like in a table. 00455 # @brief In the case of latex, it will just be the same. 00456 # @param size : (nb of lines, nb of columns) 00457 # @param col_widths : [int] 00458 # @param content : [strings] 00459 # @options : borders=0|1|2|3... (not implemented yet) 00460 # @options : unit='inch' etc. (check the possibilities...) 00461 def write_layout(self, size, col_widths, content, **options): 00462 if self.redirect_output_to_str: 00463 return translator.create_table(size, 00464 content, 00465 col_fmt=col_widths, 00466 **options) 00467 else: 00468 self.out.write(translator.create_table(size, 00469 content, 00470 col_fmt=col_widths, 00471 **options)) 00472 00473 00474 00475 00476 00477 # -------------------------------------------------------------------------- 00478 ## 00479 # @brief Creates a LaTeX string of the given object 00480 def type_string(self, objct, **options): 00481 if isinstance(objct, Printable): 00482 core.base_calculus.expression_begins = True 00483 return objct.into_str(**options) 00484 elif is_.a_number(objct) or is_.a_string(objct): 00485 return str(objct) 00486 else: 00487 raise error.UncompatibleType(objct, "String|Number|Printable") 00488 00489 00490 00491 00492 00493 # -------------------------------------------------------------------------- 00494 ## 00495 # @brief Draws a horizontal dashed line 00496 def insert_dashed_hline(self, **options): 00497 return "\\begin{tikzpicture}[x=2cm]" \ 00498 + "\draw[black,line width=0.5pt,dashed] (0,0)--(9,0);" \ 00499 + "\end{tikzpicture}" + "\n" 00500 00501 00502 00503 00504 00505 # -------------------------------------------------------------------------- 00506 ## 00507 # @brief Puts a vertical space (default 1 cm) 00508 def insert_vspace(self, **options): 00509 return "\\vspace{1 cm}" 00510 00511 00512 00513 00514 00515 # -------------------------------------------------------------------------- 00516 ## 00517 # @brief Draws and inserts the picture of the drawable_arg 00518 def insert_picture(self, drawable_arg, **options): 00519 if not isinstance(drawable_arg, Drawable): 00520 raise error.WrongArgument(str(drawable_arg), 'a Drawable') 00521 00522 if self.create_pic_files: 00523 drawable_arg.into_pic(**options) 00524 else: 00525 drawable_arg.into_pic(create_pic_files='no', **options) 00526 00527 return "\includegraphics[scale=1]{" \ 00528 + drawable_arg.eps_filename \ 00529 + "}" + "\\newline" + "\n" 00530 00531 00532 00533 00534 00535 # -------------------------------------------------------------------------- 00536 ## 00537 # @brief Sets the font_size_offset field 00538 def set_font_size_offset(self, arg): 00539 if not is_.an_integer(arg): 00540 raise error.UncompatibleType(objct, "Integer") 00541 00542 else: 00543 self.font_size_offset = arg 00544 00545 00546 # -------------------------------------------------------------------------- 00547 ## 00548 # @brief Sets the redirect_output_to_str field to True or False 00549 def set_redirect_output_to_str(self, arg): 00550 if type(arg) == bool: 00551 self.redirect_output_to_str = arg 00552 else: 00553 raise error.OutOfRangeArgument(arg, " boolean ") 00554 00555 00556 00557 00558 00559