mathmaker  0.6(alpha)
mamk_misc/doc/mathmaker4doxygen/machine/LaTeX.py
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