def __init__(self, plgmgr): """Initializes the LaTeX object @param plgmgr: pluginmanger for this object """ plugin.Plugin.__init__(self, plgmgr) self._stc = None self._id = ed_glob.ID_TEX_GEN self._dstyle = StyleItem() self._cmds = dict()
def DuplicateStyleDict(style_dict): """Duplicates the style dictionary to make a true copy of it, as simply assigning the dictionary to two different variables only copies a reference leaving both variables pointing to the same object. @param style_dict: dictionary of tags->StyleItems @return: a copy of the given styleitem dictionary """ new_dict = dict() for tag in style_dict: new_dict[tag] = StyleItem() is_ok = new_dict[tag].SetAttrFromStr(unicode(style_dict[tag])) if not is_ok: new_dict[tag].null = True return new_dict
def GenerateStyleSheet(self): """Generates a style sheet from the dialogs style data @return: The dictionary of L{StyleItem} in self.styles_new transformed into a string that is in Editra Style Sheet format. """ sty_sheet = list() ditem = self.styles_new.get('default_style', StyleItem()) dvals = ';\n\t\t'.join( [item.replace(',', ' ') for item in ditem.GetAsList()]) + ';' sty_sheet.append(''.join(['default_style {\n\t\t', dvals, '\n\n}\n\n'])) for tag, item in self.styles_new.iteritems(): if item.IsNull() or tag == 'default_style': continue stage1 = wx.EmptyString for attr in ('fore', 'back', 'face', 'size'): ival = item.GetNamedAttr(attr) if ival is None or ival == ditem.GetNamedAttr(attr): continue stage1 = ''.join((stage1, attr, u':', ival.replace(',', ' '), u';')) if len(stage1): sty_sheet.append(tag + u" {\n") stage2 = u"\t\t" + stage1[0:-1].replace(u";", u";\n\t\t") + u";" sty_sheet.append(stage2) sty_sheet.append(u"\n}\n\n") return u"".join(sty_sheet)
def GenerateStyleSheet(self): """Generates a style sheet from the dialogs style data @return: The dictionary of L{StyleItem} in self.styles_new transformed into a string that is in Editra Style Sheet format. """ sty_sheet = list() ditem = self.styles_new.get('default_style', StyleItem()) dvals = ';\n\t\t'.join( [item.replace(',', ' ') for item in ditem.GetAsList()]) + ';' sty_sheet.append(''.join(['default_style {\n\t\t', dvals, '\n\n}\n\n'])) tags = sorted(self.styles_new.keys()) for tag in tags: item = self.styles_new[tag] if item.IsNull() or tag == 'default_style': continue stage1 = wx.EmptyString for attr in ('fore', 'back', 'face', 'size'): ival = item.GetNamedAttr(attr) if attr in ('fore', 'back'): ival = ival.upper() if ival is None or ival == ditem.GetNamedAttr(attr): continue stage1 = ''.join((stage1, attr, u':', ival.replace(',', ' '), u';')) # Add any modifiers to the modifier tag modifiers = item.GetModifiers() if len(modifiers): stage1 += (u"modifiers:" + modifiers + u";").replace(',', ' ') # If the StyleItem had any set attributes add it to the stylesheet if len(stage1): sty_sheet.append(tag + u" {\n") stage2 = u"\t\t" + stage1[0:-1].replace(u";", u";\n\t\t") + u";" sty_sheet.append(stage2) sty_sheet.append(u"\n}\n\n") return u"".join(sty_sheet)
class LaTeX(plugin.Plugin): """Creates a LaTeX document object from the contents of the supplied document reference. @todo: performance improvements and wordwrap in generated document """ plugin.Implements(GeneratorI) def __init__(self, plgmgr): """Initializes the LaTeX object @param plgmgr: pluginmanger for this object """ plugin.Plugin.__init__(self) # Attributes self._stc = None self._id = ed_glob.ID_TEX_GEN self._dstyle = StyleItem() self._cmds = dict() def CreateCmdName(self, name): """Creates and returns a proper cmd name @param name: name to construct command from @return: latex formated command string """ name = name.replace('_', '') tmp = list() alpha = "ABCDEFGHIJ" for char in name: if char.isdigit(): tmp.append(alpha[int(char)]) else: tmp.append(char) return "".join(tmp) def GenDoc(self): """Generates the document body of the LaTeX document @returns: the main body of the reference document marked up with latex """ tex = list() tmp = u'' start = parse_pos = 0 last_pos = self._stc.GetLineEndPosition(self._stc.GetLineCount()) # Define the default style self.RegisterStyleCmd('default_style', \ self._stc.GetItemByName('default_style')) # Get Document start point info last_id = self._stc.GetStyleAt(parse_pos) tmp = self.TransformText( self._stc.GetTextRange(parse_pos, parse_pos + 1)) tag = self._stc.FindTagById(last_id) if tag != wx.EmptyString: self.RegisterStyleCmd(tag, self._stc.GetItemByName(tag)) # Optimizations stc = self._stc GetStyleAt = stc.GetStyleAt GetTextRange = stc.GetTextRange TransformText = self.TransformText # Build LaTeX for parse_pos in xrange(last_pos + 1): curr_id = GetStyleAt(parse_pos) if parse_pos > 1: # This is the performance bottleneck, changeing the text # collection to when the style changes is much faster as # it only needs to be done once per style section instead # of once per character. Doing that however causes problems # with the style and resulting document formatting. tmp = TransformText(GetTextRange((parse_pos - 1), parse_pos)) if curr_id == 0 and GetStyleAt(parse_pos + 1) == last_id: curr_id = last_id # If style region has changed close section if curr_id != last_id or tmp[-1] == "\n": tmp_tex = TransformText(GetTextRange(start, parse_pos)) # tmp_tex = u"".join(tmp) if tag == "operator_style" or \ (tag == "default_style" and \ tmp_tex.isspace() and len(tmp_tex) <= 2): tex.append(tmp_tex) else: if "\\\\\n" in tmp_tex: tmp_tex = tmp_tex.replace("\\\\\n", "") tmp2 = "\\%s{%s}\\\\\n" else: tmp2 = "\\%s{%s}" cmd = self.CreateCmdName(tag) if cmd in [None, wx.EmptyString]: cmd = "defaultstyle" tex.append(tmp2 % (cmd, tmp_tex)) last_id = curr_id tag = stc.FindTagById(last_id) if tag not in [None, wx.EmptyString]: self.RegisterStyleCmd(tag, stc.GetItemByName(tag)) tmp = list() start = parse_pos # Case for unstyled documents if tex == wx.EmptyString: tex.append(self.TransformText(stc.GetText())) return "\\begin{document}\n%s\n\\end{document}" % "".join(tex) def Generate(self, stc_doc): """Generates the LaTeX document @param stc_doc: text control to generate latex from @return: the reference document marked up in LaTeX. """ self._stc = stc_doc default_si = self._stc.GetItemByName('default_style') self._dstyle.SetBack(default_si.GetBack().split(',')[0]) self._dstyle.SetFore(default_si.GetFore().split(',')[0]) self._dstyle.SetFace(default_si.GetFace().split(',')[0]) self._dstyle.SetSize(default_si.GetSize().split(',')[0]) body = self.GenDoc() preamble = self.GenPreamble() return ("tex", u"".join([preamble, body])) def GenPreamble(self): """Generates the Preamble of the document @return: the LaTeX document preamble """ # Preamble template pre = ( "%% \iffalse meta-comment\n" "%%\n%% Generated by Editra %s\n" "%% This is generator is Very Experimental.\n" "%% The code should compile in most cases but there may\n" "%% be some display issues when rendered.\n" "%%\n%%\n\n" "\\documentclass[11pt, a4paper]{article}\n" "\\usepackage[a4paper, margin=2cm]{geometry}\n" "\\usepackage[T1]{fontenc}\n" # "\\usepackage{ucs}\n" # "\\usepackage[utf8]{inputenc}\n" "\\usepackage{color}\n" "\\usepackage{alltt}\n" "\\usepackage{times}\n") % ed_glob.VERSION # Set the background color pre += ("\\pagecolor[rgb]{%s}\n" % \ self.HexToRGB(self._dstyle.GetBack())) pre += "\\parindent=0in\n\n" # Insert all styling commands pre += "%% Begin Styling Command Definitions" for cmd in self._cmds: pre += ("\n" + self._cmds[cmd]) pre += "\n%% End Styling Command Definitions\n\n" return pre def GetId(self): """Returns the menu identifier for the LaTeX generator @return: id of that identifies this generator """ return self._id def GetMenuEntry(self, menu): """Returns the Menu control for the LaTeX generator @param menu: menu to create MenuItem for """ return wx.MenuItem(menu, self._id, _("Generate %s") % u"LaTeX", _("Generate an %s version of the " \ "current document") % u"LaTeX") def HexToRGB(self, hex_str): """Returns a comma separated rgb string representation of the input hex string. 1.0 = White, 0.0 = Black. @param hex_str: hex string to convert to latex rgb format """ r_hex = hex_str if r_hex[0] == u"#": r_hex = r_hex[1:] ldiff = 6 - len(r_hex) r_hex += ldiff * u"0" # Convert hex values to integer red = round(float(float(int(r_hex[0:2], 16)) / 255), 2) green = round(float(float(int(r_hex[2:4], 16)) / 255), 2) blue = round(float(float(int(r_hex[4:], 16)) / 255), 2) return "%s,%s,%s" % (str(red), str(green), str(blue)) def RegisterStyleCmd(self, cmd_name, s_item): """Registers and generates a command from the supplied StyleItem. @param cmd_name: name of command @param s_item: style item to create command for @postcondition: new styling command is created and registered for use """ cmd_name = self.CreateCmdName(cmd_name) # If we already made a command for this style return if cmd_name in self._cmds: return # Templates uline_tmp = u"\\underline{%s}" ital_tmp = u"\\emph{%s}" bold_tmp = u"\\textbf{%s}" fore_tmp = u"\\textcolor[rgb]{%s}{%s}" back_tmp = u"\\colorbox[rgb]{%s}{#1}" cmd_tmp = u"\\newcommand{%s}[1]{%s}" # Get Style Attributes fore = s_item.GetFore() if fore == wx.EmptyString: fore = self._dstyle.GetFore() back = s_item.GetBack() if back == wx.EmptyString: back = self._dstyle.GetBack() face = s_item.GetFace() if face == wx.EmptyString: face = self._dstyle.GetFace() size = s_item.GetSize() if size == wx.EmptyString: size = self._dstyle.GetSize() back = back_tmp % self.HexToRGB(back.split(u',')[0]) fore = fore_tmp % (self.HexToRGB(fore.split(u',')[0]), back) if u"bold" in unicode(s_item): fore = bold_tmp % fore if u"underline" in unicode(s_item): fore = uline_tmp % fore if u"italic" in unicode(s_item): fore = ital_tmp % fore cmd = cmd_tmp % ( (u"\\" + cmd_name), u"\\texttt{\\ttfamily{%s}}" % fore) self._cmds[cmd_name] = cmd def TransformText(self, txt): """Transforms the given text into LaTeX format, by escaping all special characters and sequences. @param txt: text to transform @return: txt with all special characters transformed """ ch_map = { "#": "\\#", "$": "\\$", "^": "\\^", "%": "\\%", "&": "\\&", "_": "\\_", "{": "\\{", "}": "\\}", "~": "\\~", "\\": "$\\backslash$", "\n": "\\\\\n", "@": "$@$", "<": "$<$", ">": "$>$", "-": "$-$", "|": "$|$" } tmp = list() for char in txt: tmp.append(ch_map.get(char, char)) return u''.join(tmp)
def GenerateBody(self): """Generates the body of the html from the stc's content. To do this it does a character by character parse of the stc to determine style regions and generate css and and styled spans of html in order to generate an 'exact' html reqresentation of the stc's window. @return: the body section of the html generated from the text control """ html = list() parse_pos = 0 style_start = 0 style_end = 0 last_pos = self.stc.GetLineEndPosition(self.stc.GetLineCount()) + 1 # Get Document start point info last_id = self.stc.GetStyleAt(parse_pos) tag = self.stc.FindTagById(last_id) if tag != wx.EmptyString: s_item = StyleItem() s_item.SetAttrFromStr(self.stc.GetStyleByName(tag)) self.css[tag] = CssItem(tag.split('_')[0], s_item) # Optimizations stc = self.stc GetStyleAt = stc.GetStyleAt # Build Html while parse_pos < last_pos: parse_pos += 1 curr_id = GetStyleAt(parse_pos) style_end = parse_pos # If style region has changed close section if curr_id == 0 and GetStyleAt(parse_pos + 1) == last_id: curr_id = last_id if curr_id != last_id or parse_pos == last_pos: tmp = stc.GetTextRange(style_start, style_end) tmp = self.TransformText(tmp) if tmp.isspace() or tag in ["default_style", "operator_style"]: html.append(tmp) else: tmp2 = "<span class=\"%s\">%s</span>" html.append(tmp2 % (tag.split('_')[0], tmp)) last_id = curr_id style_start = style_end tag = stc.FindTagById(last_id) if tag not in self.css: s_item = StyleItem() s_item.SetAttrFromStr(stc.GetStyleByName(tag)) self.css[tag] = CssItem(tag.split('_')[0], s_item) # Case for unstyled documents if len(html) == 0: s_item = StyleItem() s_item.SetAttrFromStr(stc.GetStyleByName('default_style')) self.css['default_style'] = CssItem('default', s_item) html.append(self.TransformText(stc.GetText())) else: self.OptimizeCss() return "<body class=\"default\">\n<pre>\n%s\n</pre>\n</body>" % \ "".join(html)