Index: test/test_parser.rb =================================================================== --- test/test_parser.rb (revision 244) +++ test/test_parser.rb (working copy) @@ -7,13 +7,19 @@ def red str SuperRedCloth.new(str).to_html end + + def red_latex str + SuperRedCloth.new(str).to_latex + end + Dir[File.join(DIR, "*.yml")].each do |testfile| testgroup = File.basename(testfile, '.yml') num = 0 YAML::load_documents(File.open(testfile)) do |doc| name = doc['name'] ? doc['name'].downcase.gsub(/[- ]/, '_') : num - define_method("test_#{testgroup}_#{name}") do - assert_html_equal doc['out'], red(doc['in']) + define_method("test_#{testgroup}_#{name}") do + assert_html_equal doc['out'], red(doc['in']) if doc['out'] + assert_equal doc['latex'], red_latex(doc['in']) if doc['latex'] end num += 1 end Index: test/textism.yml =================================================================== --- test/textism.yml (revision 244) +++ test/textism.yml (working copy) @@ -2,14 +2,16 @@ name: header one in: h1. Header 1 out:

Header 1

+latex: \section*{Header 1} --- name: header two in: h2. Header 2 out:

Header 2

+latex: \subsection*{Header 2} --- name: header three in: h3. Header 3 -out:

Header 3

+latex: \subsubsection*{Header 3} --- name: blockquote in: |- @@ -28,6 +30,16 @@

Any old text.

+latex: |+ + Any old text. + + \begin{quotation} + A block quotation. + + \end{quotation} + + Any old text. + --- in: |- # A first item @@ -41,7 +53,14 @@
  • A third item
  • A fourth item
  • - +latex: |+ + \begin{enumerate} + \item A first item + \item A second item + \item A third item + \item A fourth item + \end{enumerate} + --- in: |- * A first item @@ -56,22 +75,34 @@
  • A third item
  • A fourth item
  • - +latex: |+ + \begin{itemize} + \item A first item + \item A second item + \item A third item + \item A fourth item + \end{itemize} + --- in: _a phrase_ out:

    a phrase

    +latex: "\\emph{a phrase}\n\n" --- in: __a phrase__ out:

    a phrase

    +latex: "\\emph{a phrase}\n\n" --- in: '*a phrase*' out:

    a phrase

    +latex: "\\textbf{a phrase}\n\n" --- in: '**a phrase**' out:

    a phrase

    +latex: "\\textbf{a phrase}\n\n" --- in: Nabokov's ??Pnin?? out:

    Nabokov’s Pnin

    +latex: "Nabokov's \\begin{quote}Pnin\\end{quote}\n\n" --- name: del part of word in: 'A very [-extra-]ordinary day.' Index: ext/superredcloth_scan/superredcloth_scan.rl =================================================================== --- ext/superredcloth_scan/superredcloth_scan.rl (revision 244) +++ ext/superredcloth_scan/superredcloth_scan.rl (working copy) @@ -11,7 +11,7 @@ #include #include "superredcloth.h" -VALUE super_ParseError, super_RedCloth, super_HTML; +VALUE super_ParseError, super_RedCloth, super_HTML, super_LATEX; %%{ @@ -203,7 +203,18 @@ return superredcloth_transform2(super_HTML, self); } + static VALUE +superredcloth_to_latex(self) + VALUE self; +{ + char *pe, *p; + int len = 0; + + return superredcloth_transform2(super_LATEX, self); +} + +static VALUE superredcloth_to(self, formatter) VALUE self, formatter; { @@ -217,7 +228,9 @@ { super_RedCloth = rb_define_class("SuperRedCloth", rb_cString); rb_define_method(super_RedCloth, "to_html", superredcloth_to_html, 0); + rb_define_method(super_RedCloth, "to_latex", superredcloth_to_latex, 0); rb_define_method(super_RedCloth, "to", superredcloth_to, 1); super_ParseError = rb_define_class_under(super_RedCloth, "ParseError", rb_eException); - super_HTML = rb_define_module_under(super_RedCloth, "HTML"); + super_HTML = rb_define_module_under(super_RedCloth, "HTML"); + super_LATEX = rb_define_module_under(super_RedCloth, "LATEX"); } Index: lib/superredcloth.rb =================================================================== --- lib/superredcloth.rb (revision 244) +++ lib/superredcloth.rb (working copy) @@ -192,3 +192,217 @@ txt.gsub(/&/, '&') end end + + +class << SuperRedCloth::LATEX + def pba(opts) + atts = '' + opts[:"text-align"] = opts.delete(:align) + opts[:style] += ';' if opts[:style] && (opts[:style][-1..-1] != ';') + [:float, :"text-align", :"vertical-align"].each do |a| + opts[:style] = "#{a}:#{opts[a]};#{opts[:style]}" if opts[a] + end + [:"padding-right", :"padding-left"].each do |a| + opts[:style] = "#{a}:#{opts[a]}em;#{opts[:style]}" if opts[a] + end + [:style, :class, :lang, :id, :colspan, :rowspan, :title, :start, :align].each do |a| + atts << " #{a}=\"#{ opts[a] }\"" if opts[a] + end + atts + end + + # commands + { :h1 => 'section*', + :h2 => 'subsection*', + :h3 => 'subsubsection*', + :h4 => 'textbf', + :h5 => 'textbf', + :h6 => 'textbf', + :strong => 'textbf', + :em => 'emph', + :i => 'emph', + :b => 'textbf', + :ins => 'underline', + :del => 'sout', + :acronym => 'MakeUppercase', + :caps => 'MakeUppercase', + }.each do |m,tag| + define_method(m) do |opts| + "\\#{tag}{#{opts[:text]}}" + end + end + + { :sup => '\ensuremath{^\textrm{#1}}', + :sub => '\ensuremath{_\textrm{#1}}', + }.each do |m, expr| + define_method(m) do |opts| + expr.sub('#1', opts[:text]) + end + end + + # environments + { :pre => 'verbatim', + :code => 'verbatim', + :cite => 'quote', + }.each do |m, env| + define_method(m) do |opts| + "\\begin{#{env}}#{opts[:text]}\\end{#{env}}" + end + end + + # ignore (or find a good solution later) + [ :span, + :div, + ].each do |m| + define_method(m) do |opts| + opts[:text].to_s + end + end + + def del_phrase(opts) + " #{del(opts)}" + end + + { :ol => 'enumerate', + :ul => 'itemize', + }.each do |m, env| + define_method("#{m}_open") do |opts| + opts[:block] = true + "\\begin{#{env}}\n" + end + define_method("#{m}_close") do |opts| + "#{li_close}\\end{#{env}}\n\n" + end + end + + def li_open(opts) + "#{li_close unless opts.delete(:first)}\t\\item #{opts[:text]}" + end + + def li_close(opts=nil) + "\n" + end + + def ignore(opts) + opts[:text] + end + alias_method :notextile, :ignore + + def para(txt) + txt + end + + def p(opts) + opts[:text] + "\n\n" + end + + def td(opts) + "\t\t\t#{opts[:text]} &\n" + end + + def tr_open(opts) + "\t\t" + end + + def tr_close(opts) + "\t\t\\\\\n" + end + + # FIXME: we need to know the column count before opening tabular context. + def table_open(opts) + "\\begin{align*}\n" + end + + def table_close(opts) + "\t\\end{align*}\n" + end + + def bc_open(opts) + opts[:block] = true + "\\begin{verbatim}\n" + end + + def bc_close(opts) + "\\end{verbatim}\n" + end + + def bq_open(opts) + opts[:block] = true + "\\begin{quotation}\n" + end + + def bq_close(opts) + "\\end{quotation}\n\n" + end + + def link(opts) + "\\href{#{opts[:href]}}{#{opts[:name]}}" + end + + # FIXME: use includegraphics with security verification + def image(opts) + "" + end + + def footno(opts) + # TODO: insert a placeholder until we know the footnote content. + # For this to work, we need some kind of post-processing... + "\\footnotemark[#{opts[:text]}]" + end + + def fn(opts) + "\\footnotetext[#{opts[:id]}]{#{opts[:text]}}" + end + + def snip(opts) + "\\begin{verbatim}#{opts[:text]}\\end{verbatim}" + end + + def quote1(opts) + "`#{opts[:text]}'" + end + + def quote2(opts) + "``#{opts[:text]}\"" + end + + def ellipsis(opts) + "#{opts[:text]}\\ldots" + end + + # TODO: these should use Latex equivalents + def emdash(opts) + "--" + end + + def endash(opts) + " - " + end + + def arrow(opts) + "\\rightarrow" + end + + def trademark(opts) + "\\texttrademark" + end + + def registered(opts) + "\\textregistered" + end + + def copyright(opts) + "\\copyright" + end + + # TODO: what do we do with unicode entities ? + def entity(opts) + "&#{opts[:text]}" + end + + # ? + def dim(opts) + space = opts[:space] ? " " : '' + "#{opts[:x]}#{space}×#{space}" + end +end