changeset 22113:b6f482e29afd

New functions publish.m and grabcode.m (patch #9048). * scripts/general/module.mk: Add entries for the new funtions. * scripts/general/grabcode.m: New function. * scripts/general/publish.m: New function. * scripts/general/private/__publish_html_output__.m: New function. * scripts/general/private/__publish_latex_output__.m: New function. * scripts/help/__unimplemented__.m: Remove entries publish and grabcode. * NEWS: Announce new functions. * doc/interpreter/func.txi: Add documentation for the new functions. * test/module.mk: New entry for test module publish. * test/publish/module.mk: New entries for publish tests. * test/publish/publish.tst: New test file, to run all test scripts on publish and grabcode. * test/publish/test_script.m: New test script. * test/publish/test_script_code_only.m: New test script. * test/publish/test_script_empty.m: New test script. * test/publish/test_script_example.m: New test script. * test/publish/test_script_head_only.m: New test script.
author Kai T. Ohlhus <k.ohlhus@gmail.com>
date Fri, 15 Jul 2016 11:46:16 +0200
parents 03764eef9f7c
children c3b5f26721e1
files NEWS doc/interpreter/func.txi scripts/general/grabcode.m scripts/general/module.mk scripts/general/private/__publish_html_output__.m scripts/general/private/__publish_latex_output__.m scripts/general/publish.m scripts/help/__unimplemented__.m test/module.mk test/publish/module.mk test/publish/publish.tst test/publish/test_script.m test/publish/test_script_code_only.m test/publish/test_script_empty.m test/publish/test_script_example.m test/publish/test_script_head_only.m
diffstat 15 files changed, 2279 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Thu Jul 14 19:56:44 2016 -0400
+++ b/NEWS	Fri Jul 15 11:46:16 2016 +0200
@@ -93,6 +93,11 @@
  ** The graphics property 'graphicssmothing' for figures now controls whether
     anti-aliasing will be use for lines.  The default is "on".
 
+ ** The publish function allows easy publication of Octave script files
+    in HTML or other formats, including figures and output created by this
+    script.  It comes with its counterpart grabcode, which lets one literally
+    grab the HTML published code from a remote website, for example.
+
  ** Other new functions added in 4.2:
 
       audioformats
--- a/doc/interpreter/func.txi	Thu Jul 14 19:56:44 2016 -0400
+++ b/doc/interpreter/func.txi	Fri Jul 15 11:46:16 2016 +0200
@@ -1265,6 +1265,361 @@
 
 @DOCSTRING(source)
 
+@menu
+* Publish Octave script files::
+* Publishing Markup::
+@end menu
+
+@node Publish Octave script files
+@subsection Publish Octave script files
+
+The function @code{publish} provides a dynamic possibility to document your
+script file.  Unlike static documentation, @code{publish} runs the script
+file, saves any figures and output while running the script, and presents them
+alongside static documentation in a desired output format.  The static
+documentation can make use of @ref{Publishing Markup} to enhance and
+customize the output.
+
+@DOCSTRING(publish)
+
+The counterpart to @code{publish} is @code{grabcode}:
+
+@DOCSTRING(grabcode)
+
+@node Publishing Markup
+@subsection Publishing Markup
+
+@menu
+* Using Publishing Markup in script files::
+* Text formatting::
+* Sections::
+* Preformatted code::
+* Preformatted text::
+* Bulleted lists::
+* Numbered lists::
+* Including file content::
+* Including graphics::
+* Including URLs::
+* Mathematical equations::
+* HTML markup::
+* LaTeX markup::
+@end menu
+
+@node Using Publishing Markup in script files
+@subsubsection Using Publishing Markup in script files
+
+To use Publishing Markup, start by typing @samp{##} or @samp{%%} at the
+beginning of a new line.  For Matlab compatibility @samp{%%} is treated the
+same way as @samp{%%}.
+
+The lines following @samp{##} or @samp{%%} start with only one of either
+@samp{#} or @samp{%} followed by at least one space.  These lines are
+interpreted as section.  A section ends with the first line not starting
+with @samp{#} or @samp{%} or the end of the document is reached.
+
+A section starting in the first line of the document, followed by another
+start of a section that might be empty, is interpreted as a document
+title and introduction text.
+
+See the example below for clarity:
+
+@example
+@group
+%% Headline title
+%
+% Some *bold*, _italic_, or |monospaced| Text with
+% a <http://www.octave.org link to GNU Octave>.
+%%
+
+# "Real" Octave commands to be evaluated
+sombrero ()
+
+## Octave comment style supported as well
+#
+# * Bulleted list item 1
+# * Bulleted list item 2
+#
+# # Numbered list item 1
+# # Numbered list item 2
+@end group
+@end example
+
+@node Text formatting
+@subsubsection Text formatting
+
+Basic text formatting is supported inside sections, see the example
+given below:
+
+@example
+@group
+##
+# @b{*bold*}, @i{_italic_}, or |monospaced| Text
+@end group
+@end example
+
+Additionally two trademark symbols are supported, just embrace the letters
+@samp{TM} or @samp{R}.
+
+@example
+@group
+##
+# (TM) or (R)
+@end group
+@end example
+
+@node Sections
+@subsubsection Sections
+
+A section is started by typing @samp{##} or @samp{%%} at the beginning of
+a new line.  A section title can be provided by writing it, separated by a
+space, in the first line after @samp{##} or @samp{%%}.  Without a section
+title, the section is interpreted as a continuation of the previous section.
+For Matlab compatibility @samp{%%} is treated the same way as @samp{%%}.
+
+@example
+@group
+some_code ();
+
+## Section 1
+#
+## Section 2
+
+some_code ();
+
+##
+# Still in section 2
+
+some_code ();
+
+### Section 3
+#
+#
+@end group
+@end example
+
+@node Preformatted code
+@subsubsection Preformatted code
+
+To write preformatted code inside a section, indent the code by three
+spaces after @samp{#} at the beginning of each line and leave the lines
+above and below the code blank, except for @samp{#} at the beginning of
+those lines.
+
+@example
+@group
+##
+# This is a syntax highlighted for-loop:
+#
+#   for i = 1:5
+#     disp (i);
+#   endfor
+#
+# And more usual text.
+@end group
+@end example
+
+@node Preformatted text
+@subsubsection Preformatted text
+
+To write preformatted text inside a section, indent the code by two spaces
+after @samp{#} at the beginning of each line and leave the lines above and
+below the preformatted text blank, except for @samp{#} at the beginning of
+those lines.
+
+@example
+@group
+##
+# This following text is preformatted:
+#
+#  "To be, or not to be: that is the question:
+#  Whether 'tis nobler in the mind to suffer
+#  The slings and arrows of outrageous fortune,
+#  Or to take arms against a sea of troubles,
+#  And by opposing end them? To die: to sleep;"
+#
+#  --"Hamlet" by W. Shakespeare
+@end group
+@end example
+
+@node Bulleted lists
+@subsubsection Bulleted lists
+
+To create a bulleted list, type
+
+@example
+@group
+##
+#
+# * Bulleted list item 1
+# * Bulleted list item 2
+#
+@end group
+@end example
+
+to get output like
+
+@itemize @bullet
+@item Bulleted list item 1
+@item Bulleted list item 2
+@end itemize
+
+Notice the blank lines, except for the @samp{#} or @samp{%} before and
+after the bulleted list!
+
+@node Numbered lists
+@subsubsection Numbered lists
+
+To create a numbered list, type
+
+@example
+@group
+##
+#
+# # Numbered list item 1
+# # Numbered list item 2
+#
+@end group
+@end example
+
+to get output like
+
+@enumerate
+@item Numbered list item 1
+@item Numbered list item 2
+@end enumerate
+
+Notice the blank lines, except for the @samp{#} or @samp{%} before and
+after the numbered list!
+
+@node Including file content
+@subsubsection Including file content
+
+To include the content of an external file, e.g. a file called
+@samp{my_function.m} at the same location as the published Octave script,
+use the following syntax to include it with Octave syntax highlighting.
+
+Alternatively, you can write the full or relative path to the file.
+
+@example
+@group
+##
+#
+# <include>my_function.m</include>
+#
+# <include>/full/path/to/my_function.m</include>
+#
+# <include>../relative/path/to/my_function.m</include>
+#
+@end group
+@end example
+
+@node Including graphics
+@subsubsection Including graphics
+
+To include external graphics, e.g. a graphic called @samp{my_graphic.png}
+at the same location as the published Octave script, use the following syntax.
+
+Alternatively, you can write the full path to the graphic.
+
+@example
+@group
+##
+#
+# <<my_graphic.png>>
+#
+# <</full/path/to/my_graphic.png>>
+#
+# <<../relative/path/to/my_graphic.png>>
+#
+@end group
+@end example
+
+@node Including URLs
+@subsubsection Including URLs
+
+Basically, a URL is written between an opening @samp{<} and a closing @samp{>}
+angle.
+
+@example
+@group
+##
+# <http://www.octave.org>
+@end group
+@end example
+
+Text that is within these angles and separated by at least one space from the
+URL is a displayed text for the link.
+
+@example
+@group
+##
+# <http://www.octave.org GNU Octave>
+@end group
+@end example
+
+A link starting with @samp{<octave:} followed by the name of a GNU Octave
+function, optionally with a displayed text, results in a link to the online
+GNU Octave documentations function index.
+
+@example
+@group
+##
+# <octave:DISP The display function>
+@end group
+@end example
+
+@node Mathematical equations
+@subsubsection Mathematical equations
+
+One can insert LaTeX inline math, surrounded by single @samp{$} signs, or
+displayed math, surrounded by double @samp{$$} signs, directly inside
+sections.
+
+@example
+@group
+##
+# Some shorter inline equation $e^@{ix@} = \cos x + i\sin x$.
+#
+# Or more complicated formulas as displayed math:
+# $$e^x = \lim_@{n\rightarrow\infty@}\left(1+\dfrac@{x@}@{n@}\right)^@{n@}.$$
+@end group
+@end example
+
+@node HTML markup
+@subsubsection HTML markup
+
+If the published output is a HTML report, you can insert HTML markup,
+that is only visible in this kind of output.
+
+@example
+@group
+##
+# <html>
+# <table style="border:1px solid black;">
+# <tr><td>1</td><td>2</td></tr>
+# <tr><td>3</td><td>3</td></tr>
+# </html>
+@end group
+@end example
+
+@node LaTeX markup
+@subsubsection LaTeX markup
+
+If the published output is a LaTeX or PDF report, you can insert LaTeX markup,
+that is only visible in this kind of output.
+
+@example
+@group
+##
+# <latex>
+# Some output only visible in \LaTeX or PDF reports.
+# \begin@{equation@}
+# e^x = \lim\limits_@{n\rightarrow\infty@}\left(1+\dfrac@{x@}@{n@}\right)^@{n@}
+# \end@{equation@}
+# </latex>
+@end group
+@end example
+
 @node Function Handles Anonymous Functions Inline Functions
 @section Function Handles, Anonymous Functions, Inline Functions
 @cindex handle, function handles
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/general/grabcode.m	Fri Jul 15 11:46:16 2016 +0200
@@ -0,0 +1,105 @@
+## Copyright (C) 2016 Kai T. Ohlhus <k.ohlhus@gmail.com>
+##
+## This file is part of Octave.
+##
+## Octave is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {} {} grabcode (@var{url})
+## @deftypefnx {} {@var{code_str} =} grabcode (@var{url})
+##
+## Grab by the @code{publish} function generated HTML reports from Octave
+## script files.
+##
+## The input parameter @var{url} must point to a local or remote HTML file
+## with extension @samp{.htm} or @samp{.html} which was generated by the
+## @code{publish} function.  With any other HTML file this will not work!
+##
+## If no return value is given, the grabbed code is saved to a temporary
+## file and opened in the default editor.
+##
+## NOTE: You have to save the file at another location with arbitrary name,
+## otherwise any grabbed code will be lost!
+##
+## With a return value given, the grabbed code will be returned as string
+## @var{code_str}.
+##
+## An example:
+##
+## @example
+## @group
+## publish ("my_script.m");
+## grabcode ("html/my_script.html");
+## @end group
+## @end example
+##
+## The example above publishes @samp{my_script.m} by default to
+## @samp{html/my_script.html}.  Afterwards this published Octave script
+## is grabbed to edit its content in a new temporary file.
+##
+## @seealso{publish}
+## @end deftypefn
+
+function varargout = grabcode (url)
+  narginchk (1, 1);
+  nargoutchk (0, 1);
+
+  [~,~,ext] = fileparts (url);
+  if (! strncmp (ext, ".htm", 4))
+    error ("grabcode: URL should point to a published \".html\"-file");
+  endif
+
+  ## If url is a local file
+  if (exist (url) == 2)
+    oct_code = fileread (url);
+  ## Otherwise try to read a url
+  else
+    [oct_code, success, message] = urlread (url);
+    if (! success)
+      error (["grabcode: ", message]);
+    endif
+  endif
+
+  ## Extract relevant part
+  start_str = "##### SOURCE BEGIN #####";
+  end_str = "##### SOURCE END #####";
+  oct_code = oct_code(strfind (oct_code, start_str) + length(start_str) + 1: ...
+    strfind (oct_code, end_str)-1);
+
+  ## Return Octave code string ...
+  if (nargout == 1)
+    varargout{1} = oct_code;
+  ## ... or open temporary file in editor
+  else
+    fname = [tempname(), ".m"];
+    fid = fopen (fname, "w");
+    if (fid < 0)
+      error ("grabcode: cannot open temporary file");
+    endif
+    fprintf (fid, "%s", oct_code);
+    fclose (fid);
+    edit (fname);
+    warndlg (["grabcode: Make sure to save the temporary file\n\n\t", ...
+      fname, "\n\nto a location of your choice. ", ...
+      "Otherwise all grabbed code will be lost!"]);
+  endif
+endfunction
+
+## Bad function calls
+
+%!error grabcode ()
+%!error grabcode (1)
+%!error grabcode ("html/test_script.html", "pdf")
+%!error [str1, str2] = grabcode ("html/test_script.html")
\ No newline at end of file
--- a/scripts/general/module.mk	Thu Jul 14 19:56:44 2016 -0400
+++ b/scripts/general/module.mk	Fri Jul 15 11:46:16 2016 +0200
@@ -4,6 +4,8 @@
 
 scripts_general_PRIVATE_FCN_FILES = \
   scripts/general/private/__isequal__.m \
+  scripts/general/private/__publish_html_output__.m \
+  scripts/general/private/__publish_latex_output__.m \
   scripts/general/private/__splinen__.m
 
 scripts_general_FCN_FILES = \
@@ -35,6 +37,7 @@
   scripts/general/flipdim.m \
   scripts/general/fliplr.m \
   scripts/general/flipud.m \
+  scripts/general/grabcode.m \
   scripts/general/gradient.m \
   scripts/general/idivide.m \
   scripts/general/inputParser.m \
@@ -60,6 +63,7 @@
   scripts/general/polyarea.m \
   scripts/general/postpad.m \
   scripts/general/prepad.m \
+  scripts/general/publish.m \
   scripts/general/quadgk.m \
   scripts/general/quadl.m \
   scripts/general/quadv.m \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/general/private/__publish_html_output__.m	Fri Jul 15 11:46:16 2016 +0200
@@ -0,0 +1,283 @@
+function outstr = __publish_html_output__ (varargin)
+  ##
+  ## Types to handle are:
+  ##
+  ## * "header" (title_str, intro_str, toc_cstr)
+  ## * "footer" ()
+  ## * "code" (str)
+  ## * "code_output" (str)
+  ## * "section" (str)
+  ## * "preformatted_code" (str)
+  ## * "preformatted_text" (str)
+  ## * "bulleted_list" (cstr)
+  ## * "numbered_list" (cstr)
+  ## * "graphic" (str)
+  ## * "html" (str)
+  ## * "latex" (str)
+  ## * "text" (str)
+  ## * "bold" (str)
+  ## * "italic" (str)
+  ## * "monospaced" (str)
+  ## * "link" (url_str, url_str, str)
+  ## * "TM" ()
+  ## * "R" ()
+  ##
+  eval (["outstr = handle_", varargin{1}, " (varargin{2:end});"]);
+endfunction
+
+function outstr = handle_header (title_str, intro_str, toc_cstr)
+  mathjax_str = ["<script type=\"text/x-mathjax-config\">\n", ...
+    "MathJax.Hub.Config({\n", ...
+    "  tex2jax: {inlineMath: [['$','$'], ['\\\\(','\\\\)']]}\n", ...
+    "});\n", ...
+    "</script>\n", ...
+    "<script type=\"text/javascript\" async ", ...
+    "src=\"https://cdn.mathjax.org/mathjax/latest/MathJax.js?", ...
+    "config=TeX-MML-AM_CHTML\"></script>\n"];
+  stylesheet_str = ["<style>\n", ...
+    "body > * {\n", ...
+    "  max-width: 42em;\n", ...
+    "}\n", ...
+    "body {\n", ...
+    "  font-family: \"Roboto Condensed\", sans-serif;\n", ...
+    "  padding-left: 7.5em;\n", ...
+    "  padding-right: 7.5em;\n", ...
+    "}\n", ...
+    "pre, code {\n", ...
+    "  max-width: 50em;\n", ...
+    "  font-family: monospace;\n", ...
+    "}\n", ...
+    "pre.oct-code {\n", ...
+    "  border: 1px solid Grey;\n", ...
+    "  padding: 5px;\n", ...
+    "}\n", ...
+    "pre.oct-code-output {\n", ...
+    "  margin-left: 2em;\n", ...
+    "}\n", ...
+    "span.comment {\n", ...
+    "  color: ForestGreen;\n", ...
+    "}\n",...
+    "span.keyword {\n", ...
+    "  color: Blue;\n", ...
+    "}\n",...
+    "span.string {\n", ...
+    "  color: DarkOrchid;\n", ...
+    "}\n",...
+    "footer {\n", ...
+    "  margin-top: 2em;\n", ...
+    "  font-size: 80%;\n", ...
+    "}\n", ...
+    "a, a:visited {\n", ...
+    "  color: Blue;\n", ...
+    "}\n", ...
+    "h2 {\n", ...
+    "  font-family: \"Roboto Condensed\", serif;\n", ...
+    "  margin-top: 1.5em;\n", ...
+    "}\n", ...
+    "h2 a, h2 a:visited {\n", ...
+    "  color: Black;\n", ...
+    "}\n", ...
+    "</style>\n"];
+  outstr = ["<!DOCTYPE html>\n", ...
+    "<html>\n", ...
+    "<head>\n", ...
+    "<meta charset=\"UTF-8\">\n", ...
+    "<title>", title_str, "</title>\n", ...
+    mathjax_str, ...
+    stylesheet_str, ...
+    "</head>\n", ...
+    "<body>\n", ...
+    "<h1>", title_str, "</h1>\n", ...
+    intro_str];
+
+  if (! isempty (toc_cstr))
+    for i = 1:length(toc_cstr)
+      toc_cstr{i} = handle_link (["#node", num2str(i)], toc_cstr{i});
+    endfor
+    outstr = [outstr, "<h2>Contents</h2>", ...
+      handle_bulleted_list(toc_cstr)];
+  endif
+
+  ## Reset section counter
+  handle_section ();
+endfunction
+
+function outstr = handle_footer (m_source_str)
+  outstr = ["\n", ...
+    "<footer><hr>", ...
+    "<a href=\"http://www.octave.org\">Published with GNU Octave ", ...
+    version(), "</a></footer>\n", ...
+    "<!--\n", ...
+    "##### SOURCE BEGIN #####\n", ...
+    m_source_str, ...
+    "\n##### SOURCE END #####\n", ...
+    "-->\n", ...
+    "</body>\n", ...
+    "</html>\n"];
+endfunction
+
+function outstr = handle_code (str)
+  outstr = ["<pre class=\"oct-code\">", syntax_highlight(str), "</pre>"];
+endfunction
+
+function outstr = handle_code_output (str)
+  outstr = ["<pre class=\"oct-code-output\">", str, "</pre>"];
+endfunction
+
+function outstr = handle_section (varargin)
+  persistent counter = 1;
+  if (nargin == 0)
+    counter = 1;
+    outstr = "";
+    return;
+  endif
+  outstr = ["<h2><a id=\"node", num2str(counter), "\">", varargin{1}, ...
+    "</a></h2>"];
+  counter++;
+endfunction
+
+function outstr = handle_preformatted_code (str)
+  outstr = ["<pre class=\"pre-code\">", syntax_highlight(str), "</pre>"];
+endfunction
+
+function outstr = handle_preformatted_text (str)
+  outstr = ["<pre class=\"pre-text\">", str, "</pre>"];
+endfunction
+
+function outstr = handle_bulleted_list (cstr)
+  outstr = "<ul>";
+  for i = 1:length(cstr)
+    outstr = [outstr, "<li>", cstr{i}, "</li>"];
+  endfor
+  outstr = [outstr, "</ul>"];
+endfunction
+
+function outstr = handle_numbered_list (cstr)
+  outstr = "<ol>";
+  for i = 1:length(cstr)
+    outstr = [outstr, "<li>", cstr{i}, "</li>"];
+  endfor
+  outstr = [outstr, "</ol>"];
+endfunction
+
+function outstr = handle_graphic (str)
+  outstr = ["<img src=\"", str,"\" alt=\"", str, "\">"];
+endfunction
+
+function outstr = handle_html (str)
+  outstr = str;
+endfunction
+
+function outstr = handle_latex (str)
+  outstr = "";
+endfunction
+
+function outstr = handle_link (url_str, str)
+  outstr = ["<a href=\"", url_str,"\">", str, "</a>"];
+endfunction
+
+function outstr = handle_text (str)
+  outstr = ["<p>", str, "</p>"];
+endfunction
+
+function outstr = handle_bold (str)
+  outstr = ["<b>", str, "</b>"];
+endfunction
+
+function outstr = handle_italic (str)
+  outstr = ["<i>", str, "</i>"];
+endfunction
+
+function outstr = handle_monospaced (str)
+  outstr = ["<code>", str, "</code>"];
+endfunction
+
+function outstr = handle_TM ()
+  outstr = "&trade;";
+endfunction
+
+function outstr = handle_R ()
+  outstr = "&reg;";
+endfunction
+
+function outstr = syntax_highlight (str)
+  ## SYNTAX_HIGHLIGHT a primitive parser to add syntax highlight via <span>
+  ##   tags. Should be replaced by a better solution.
+  ##
+
+  outstr = "";
+  i = 1;
+  while (i <= length(str))
+    ## Block comment
+    if (any (strncmp (str(i:end), {"%{", "#{"}, 2)))
+      outstr = [outstr, "<span class=\"comment\">", str(i:i+1)];
+      i = i + 2;
+      while ((i <= length(str)) ...
+             && ! (any (strncmp (str(i:end), {"%}", "#}"}, 2))))
+        outstr = [outstr, str(i)];
+        i++;
+      endwhile
+      if (i < length(str))
+        outstr = [outstr, str(i:i+1), "</span>"];
+        i = i + 2;
+      else
+        outstr = [outstr, "</span>"];
+      endif
+    ## Line comment
+    elseif (any (strcmp (str(i), {"%", "#"})))
+      outstr = [outstr, "<span class=\"comment\">"];
+      while ((i <= length(str)) && (! strcmp (str(i), "\n")))
+        outstr = [outstr, str(i)];
+        i++;
+      endwhile
+      outstr = [outstr, "</span>\n"];
+      i++;
+    ## Single quoted string
+    elseif (strcmp (str(i), "'"))
+      outstr = [outstr, "<span class=\"string\">'"];
+      i++;
+      while (i <= length(str))
+        ## Ignore escaped string terminations
+        if (strncmp (str(i:end), "''", 2))
+          outstr = [outstr, "''"];
+          i = i + 2;
+        ## Is string termination
+        elseif (strcmp (str(i), "'"))
+          outstr = [outstr, "'</span>"];
+          i++;
+          break;
+        ## String content
+        else
+          outstr = [outstr, str(i)];
+          i++;
+        endif
+      endwhile
+    ## Double quoted string
+    elseif (strcmp (str(i), "\""))
+      outstr = [outstr, "<span class=\"string\">\""];
+      i++;
+      while (i <= length(str))
+        ## Is string termination
+        if (strcmp (str(i), "\"") && ! strcmp (str(i - 1), "\\"))
+          outstr = [outstr, "\"</span>"];
+          i++;
+          break;
+        ## String content
+        else
+          outstr = [outstr, str(i)];
+          i++;
+        endif
+      endwhile
+    else
+      outstr = [outstr, str(i)];
+      iskeyword ("if");
+      i++;
+    endif
+  endwhile
+  kwords = iskeyword ();
+  for i = 1:length(kwords)
+    outstr = regexprep (outstr, ...
+      ['(?!<span[^>]*?>)(\s|^)(', kwords{i},')(\s|$)(?![^<]*?<\/span>)'], ...
+      ["$1<span class=\"keyword\">$2</span>$3"]);
+  endfor
+endfunction
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/general/private/__publish_latex_output__.m	Fri Jul 15 11:46:16 2016 +0200
@@ -0,0 +1,158 @@
+function outstr = __publish_latex_output__ (varargin)
+  ##
+  ## Types to handle are:
+  ##
+  ## * "header" (title_str, intro_str, toc_cstr)
+  ## * "footer" ()
+  ## * "code" (str)
+  ## * "code_output" (str)
+  ## * "section" (str)
+  ## * "preformatted_code" (str)
+  ## * "preformatted_text" (str)
+  ## * "bulleted_list" (cstr)
+  ## * "numbered_list" (cstr)
+  ## * "graphic" (str)
+  ## * "html" (str)
+  ## * "latex" (str)
+  ## * "text" (str)
+  ## * "bold" (str)
+  ## * "italic" (str)
+  ## * "monospaced" (str)
+  ## * "link" (url_str, url_str, str)
+  ## * "TM" ()
+  ## * "R" ()
+  ##
+  eval (["outstr = handle_", varargin{1}, " (varargin{2:end});"]);
+endfunction
+
+function outstr = handle_header (title_str, intro_str, toc_cstr)
+  publish_comment = ["\n\n", ...
+    "% This document was generated by the publish-function\n", ...
+    "% from GNU Octave ", version(), "\n\n"];
+
+  latex_preamble = ["\n\n", ...
+    "\\documentclass[10pt]{article}\n", ...
+    "\\usepackage{listings}\n", ...
+    "\\usepackage{mathtools}\n", ...
+    "\\usepackage{graphicx}\n", ...
+    "\\usepackage{hyperref}\n", ...
+    "\\usepackage{xcolor}\n", ...
+    "\\usepackage{titlesec}\n", ...
+    "\\usepackage[utf8]{inputenc}\n", ...
+    "\\usepackage[T1]{fontenc}\n", ...
+    "\\usepackage{lmodern}\n"];
+
+  listings_option = ["\n\n", ...
+    "\\lstset{\n", ...
+    "language=Octave,\n", ...
+    "numbers=none,\n", ...
+    "frame=single,\n", ...
+    "tabsize=2,\n", ...
+    "showstringspaces=false,\n", ...
+    "breaklines=true}\n"];
+
+  ## Escape "_" in title_str, as many file names contain it
+  latex_head = ["\n\n", ...
+    "\\titleformat*{\\section}{\\Huge\\bfseries}\n", ...
+    "\\titleformat*{\\subsection}{\\large\\bfseries}\n", ...
+    "\\renewcommand{\\contentsname}{\\Large\\bfseries Contents}\n", ...
+    "\\setlength{\\parindent}{0pt}\n\n",...
+    "\\begin{document}\n\n", ...
+    "{\\Huge\\section*{", escape_chars(title_str),"}}\n\n", ...
+    "\\tableofcontents\n", ...
+    "\\vspace*{4em}\n\n"];
+    
+  outstr = [publish_comment, latex_preamble, listings_option, latex_head];
+endfunction
+
+function outstr = handle_footer (m_source_str)
+  outstr = ["\n\n\\end{document}\n"];
+endfunction
+
+function outstr = handle_code (str)
+  outstr = ["\\begin{lstlisting}\n", str, "\n\\end{lstlisting}\n"];
+endfunction
+
+function outstr = handle_code_output (str)
+  outstr = ["\\begin{lstlisting}", ...
+    "[language={},xleftmargin=5pt,frame=none]\n", ...
+    str, "\n\\end{lstlisting}\n"];
+endfunction
+
+function outstr = handle_section (str)
+  outstr = ["\n\n\\phantomsection\n", ...
+    "\\addcontentsline{toc}{section}{", str, "}\n", ...
+    "\\subsection*{", str, "}\n\n"];
+endfunction
+
+function outstr = handle_preformatted_code (str)
+  outstr = ["\\begin{lstlisting}\n", str, "\n\\end{lstlisting}\n"];
+endfunction
+
+function outstr = handle_preformatted_text (str)
+  outstr = ["\\begin{lstlisting}[language={}]\n", ...
+    str, "\n\\end{lstlisting}\n"];
+endfunction
+
+function outstr = handle_bulleted_list (cstr)
+  outstr = "\n\\begin{itemize}\n";
+  for i = 1:length(cstr)
+    outstr = [outstr, "\\item ", cstr{i}, "\n"];
+  endfor
+  outstr = [outstr, "\\end{itemize}\n"];
+endfunction
+
+function outstr = handle_numbered_list (cstr)
+  outstr = "\n\\begin{enumerate}\n";
+  for i = 1:length(cstr)
+    outstr = [outstr, "\\item ", cstr{i}, "\n"];
+  endfor
+  outstr = [outstr, "\\end{enumerate}\n"];
+endfunction
+
+function outstr = handle_graphic (str)
+  outstr = ["\\begin{figure}[!ht]\n", ...
+    "\\includegraphics[width=\\textwidth]{", str, "}\n", ...
+    "\\end{figure}\n"];
+endfunction
+
+function outstr = handle_html (str)
+  outstr = "";
+endfunction
+
+function outstr = handle_latex (str)
+  outstr = str;
+endfunction
+
+function outstr = handle_link (url_str, str)
+  outstr = ["\\href{", url_str,"}{", str, "}"];
+endfunction
+
+function outstr = handle_text (str)
+  outstr = ["\n\n", str, "\n\n"];
+endfunction
+
+function outstr = handle_bold (str)
+  outstr = ["\\textbf{", str, "}"];
+endfunction
+
+function outstr = handle_italic (str)
+  outstr = ["\\textit{", str, "}"];
+endfunction
+
+function outstr = handle_monospaced (str)
+  outstr = ["\\texttt{", str, "}"];
+endfunction
+
+function outstr = handle_TM ()
+  outstr = "\\texttrademark";
+endfunction
+
+function outstr = handle_R ()
+  outstr = "\\textregistered";
+endfunction
+
+function str = escape_chars (str)
+  ## Escape "_"
+  str = regexprep (str, '([^\\]|^)(_)', "$1\\_");
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/general/publish.m	Fri Jul 15 11:46:16 2016 +0200
@@ -0,0 +1,971 @@
+## Copyright (C) 2016 Kai T. Ohlhus <k.ohlhus@gmail.com>
+## Copyright (C) 2010 Fotios Kasolis <fotios.kasolis@gmail.com>
+##
+## This file is part of Octave.
+##
+## Octave is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {} {} publish (@var{filename})
+## @deftypefnx {} {} publish (@var{filename}, @var{output_format})
+## @deftypefnx {} {} publish (@var{filename}, @var{option1}, @var{value1}, @dots{})
+## @deftypefnx {} {} publish (@var{filename}, @var{options})
+## @deftypefnx {} {@var{output_file} =} publish (@var{filename}, @dots{})
+##
+## Generate reports from Octave script files in several output formats.
+##
+## The generated reports consider Publishing Markup in comments,
+## which is explained in detail in the GNU Octave manual.  Assume the
+## following example, using some Publishing Markup, to be the content
+## of a script file named @samp{example.m}:
+##
+## @example
+## @group
+## %% Headline title
+## %
+## % Some *bold*, _italic_, or |monospaced| Text with
+## % a <http://www.octave.org link to *GNU Octave*>.
+## %%
+##
+## # "Real" Octave commands to be evaluated
+## sombrero ()
+##
+## ## Octave comment style supported as well
+## #
+## # * Bulleted list item 1
+## # * Bulleted list item 2
+## #
+## # # Numbered list item 1
+## # # Numbered list item 2
+## @end group
+## @end example
+##
+## To publish this script file, type @code{publish ("example.m")}.
+##
+## With only @var{filename} given, a HTML report is generated in a
+## subdirectory @samp{html} relative to the current working directory.
+## The Octave commands are evaluated in a seperate context and any
+## figures created while executing the script file are included in the
+## report.  All formatting syntax of @var{filename} is treated according
+## to the specified output format and included in the report.
+##
+## Using @code{publish (@var{filename}, @var{output_format})} is
+## equivalent to the function call using a structure
+##
+## @example
+## @group
+## @var{options}.format = @var{output_format};
+## publish (@var{filename}, @var{options})
+## @end group
+## @end example
+##
+## which is described below.  The same holds for using option-value-pairs
+##
+## @example
+## @group
+## @var{options}.@var{option1} = @var{value1};
+## publish (@var{filename}, @var{options})
+## @end group
+## @end example
+##
+## The structure @var{options} can have the following field names.  If a
+## field name is not specified, the default value is considered:
+##
+## @itemize @bullet
+## @item
+## @samp{format} --- Output format of the published script file, one of
+##
+## @samp{html} (default), @samp{doc}, @samp{latex}, @samp{ppt},
+## @samp{xml}, or @samp{pdf}.
+##
+## The output formats @samp{doc}, @samp{ppt}, and @samp{xml} are currently
+## not supported. To generate a @samp{doc} report, open a generated
+## @samp{html} report with your office suite.
+##
+## @item
+## @samp{outputDir} --- Full path string of a directory, where the generated
+## report will be located.  If no directory is given, the report is generated
+## in a subdirectory @samp{html} relative to the current working directory.
+##
+## @item
+## @samp{stylesheet} --- Not supported, only for Matlab compatibility.
+##
+## @item
+## @samp{createThumbnail} --- Not supported, only for Matlab compatibility.
+##
+## @item
+## @samp{figureSnapMethod} --- Not supported, only for Matlab compatibility.
+##
+## @item
+## @samp{imageFormat} --- Desired format for images produced, while
+## evaluating the code.  The allowed image formats depend on the output
+## format:
+##
+## @itemize @bullet
+## @item @samp{html} and @samp{xml} --- @samp{png} (default), any other
+## image format supported by Octave
+##
+## @item @samp{latex} --- @samp{epsc2} (default), any other image format
+## supported by Octave
+##
+## @item @samp{pdf} --- @samp{jpg} (default) or @samp{bmp}, note Matlab
+## uses  @samp{bmp} as default
+##
+## @item @samp{doc} or @samp{ppt} --- @samp{png} (default), @samp{jpg},
+## @samp{bmp}, or @samp{tiff}
+## @end itemize
+##
+## @item
+## @samp{maxHeight} and @samp{maxWidth} --- Maximum height (width) of the
+## produced images in pixels.  An empty value means no restriction.  Both
+## values have to be set, to work properly.
+##
+## @samp{[]} (default), integer value @geq{} 0
+##
+## @item
+## @samp{useNewFigure} --- Use a new figure window for figures created by
+## the evaluated code.  This avoids side effects with already opened figure
+## windows.
+##
+## @samp{true} (default) or @samp{false}
+##
+## @item
+## @samp{evalCode} --- Evaluate code of the Octave source file
+##
+## @samp{true} (default) or @samp{false}
+##
+## @item
+## @samp{catchError} --- Catch errors while code evaluation and continue
+##
+## @samp{true} (default) or @samp{false}
+##
+## @item
+## @samp{codeToEvaluate} --- Octave commands that should be evaluated prior
+## to publishing the script file.  These Octave commands do not appear in the
+## generated report.
+##
+## @item
+## @samp{maxOutputLines} --- Maximum number of shown output lines of the
+## code evaluation
+##
+## @samp{Inf} (default) or integer value > 0
+##
+## @item
+## @samp{showCode} --- Show the evaluated Octave commands in the generated
+## report
+##
+## @samp{true} (default) or @samp{false}
+## @end itemize
+##
+## The returned @var{output_file} is a string with the path and file name
+## of the generated report.
+##
+## @seealso{grabcode}
+## @end deftypefn
+
+function output_file = publish (file, varargin)
+  narginchk (1, Inf);
+  nargoutchk (0, 1);
+
+  if (exist (file, "file") != 2)
+    error ("publish: FILE does not exist.");
+  endif
+
+  ## Check file extension and for an Octave script
+  [~, file_name, file_ext] = fileparts (file);
+  file_info = __which__ (file_name);
+
+  if ((! strcmp (file_ext, ".m")) || (! strcmp (file_info.type, "script")))
+    error ("publish: Only Octave script files can be published.");
+  endif
+
+  ## Check file to be parsable
+  __parse_file__ (file);
+
+  ## Get structure with necessary options
+  options = struct ();
+  if (numel (varargin) == 1)
+    ## Call: publish (file, format)
+    if (ischar (varargin{1}))
+      options.format = varargin{1};
+    ## Call: publish (file, options)
+    elseif (isstruct (varargin{1}))
+      options = varargin{1};
+    else
+      error ("publish: Invalid second argument.");
+    endif
+  ## Call: publish (file, Name1, Value1, Name2, Value2, ...)
+  elseif ((rem (numel (varargin), 2) == 0) ...
+          && (all (cellfun (@ischar, varargin))))
+    for i = 1:2:numel(varargin)
+      options = setfield (options, varargin{i}, varargin{i + 1});
+    endfor
+  else
+    error ("publish: Invalid or inappropriate arguments.");
+  endif
+
+  ##
+  ## Validate options struct
+  ##
+
+  ## Options for the output
+  if (! isfield (options, "format"))
+    options.format = "html";
+  else
+    options.format = validatestring (options.format, ...
+      {"html", "doc", "latex", "ppt", "xml", "pdf"});
+    ## TODO: implement remaining formats
+    if (any (strcmp (options.format, {"doc", "ppt", "xml"})))
+      error ("publish: Output format currently not supported");
+    endif
+  endif
+
+  if (! isfield (options, "outputDir"))
+    ## Matlab R2016a doc says default is "", but specifies to create a sub
+    ## directory named "html" in the current working directory.
+    options.outputDir = "html";
+  elseif (! ischar (options.outputDir))
+    error ("publish: OUTPUTDIR must be a string");
+  endif
+
+  if (! isfield (options, "stylesheet"))
+    options.stylesheet = "";
+  elseif (! ischar (options.stylesheet))
+    error ("publish: STYLESHEET must be a string");
+  endif
+
+  ## Options for the figures
+  if (! isfield (options, "createThumbnail"))
+    options.createThumbnail = true;
+  elseif ((! isscalar (options.createThumbnail)) ...
+          || (! isbool (options.createThumbnail)))
+    error ("publish: CREATETHUMBNAIL must be TRUE or FALSE");
+  endif
+
+  if (! isfield (options, "figureSnapMethod"))
+    options.figureSnapMethod = "entireGUIWindow";
+  else
+    options.figureSnapMethod = validatestring (options.figureSnapMethod, ...
+      {"entireGUIWindow", "print", "getframe", "entireFigureWindow"});
+    ## TODO: implement
+    warning ("publish: option FIGURESNAPMETHOD currently not supported")
+  endif
+
+  if (! isfield (options, "imageFormat"))
+    switch (options.format)
+      case "latex"
+        options.imageFormat = "epsc2";
+      case "pdf"
+        ## Note: Matlab R2016a uses bmp as default
+        options.imageFormat = "jpg";
+      otherwise
+        options.imageFormat = "png";
+    endswitch
+  elseif (! ischar (options.imageFormat))
+    error ("publish: IMAGEFORMAT must be a string");
+  else
+    ## check valid imageFormat for chosen format
+    ##   html, latex, and xml accept any imageFormat
+    switch (options.format)
+      case {"doc", "ppt"}
+        options.imageFormat = validatestring (options.imageFormat, ...
+          {"png", "jpg", "bmp", "tiff"});
+      case "pdf"
+        options.imageFormat = validatestring (options.imageFormat, ...
+          {"bmp", "jpg"});
+    endswitch
+  endif
+
+  if (! isfield (options, "maxHeight"))
+    options.maxHeight = [];
+  elseif ((! isscalar (options.maxHeight)) ...
+          || (uint64 (options.maxHeight) == 0))
+    error ("publish: MAXHEIGHT must be a positive integer");
+  else
+    options.maxHeight = uint64 (options.maxHeight);
+  endif
+
+  if (! isfield (options, "maxWidth"))
+    options.maxWidth = [];
+  elseif ((! isscalar (options.maxWidth)) ...
+          || (uint64 (options.maxWidth) == 0))
+    error ("publish: MAXWIDTH must be a positive integer");
+  else
+    options.maxWidth = uint64 (options.maxWidth);
+  endif
+
+  if (! isfield (options, "useNewFigure"))
+    options.useNewFigure = true;
+  elseif (! isbool (options.useNewFigure))
+    error ("publish: USENEWFIGURE must be TRUE or FALSE");
+  endif
+
+  ## Options for the code
+  if (!isfield (options, "evalCode"))
+    options.evalCode = true;
+  elseif ((! isscalar (options.evalCode)) || (! isbool (options.evalCode)))
+    error ("publish: EVALCODE must be TRUE or FALSE");
+  endif
+
+  if (!isfield (options, "catchError"))
+    options.catchError = true;
+  elseif ((! isscalar (options.catchError)) || (! isbool (options.catchError)))
+    error ("publish: CATCHERROR must be TRUE or FALSE");
+  endif
+
+  if (!isfield (options, "codeToEvaluate"))
+    options.codeToEvaluate = "";
+  elseif (! ischar (options.codeToEvaluate))
+    error ("publish: CODETOEVALUTE must be a string");
+  endif
+
+  if (! isfield (options, "maxOutputLines"))
+    options.maxOutputLines = Inf;
+  elseif (! isscalar (options.maxOutputLines))
+    error ("publish: MAXOUTPUTLINES must be an integer >= 0");
+  else
+    options.maxOutputLines = uint64 (options.maxOutputLines);
+  endif
+
+  if (!isfield (options, "showCode"))
+    options.showCode = true;
+  elseif ((! isscalar (options.showCode)) || (! isbool (options.showCode)))
+    error ("publish: SHOWCODE must be TRUE or FALSE");
+  endif
+
+  doc_struct.title = "";
+  doc_struct.intro = "";
+  doc_struct.body = cell ();
+  doc_struct.m_source = deblank (read_file_to_cellstr (file));
+  doc_struct.m_source_file_name = file;
+
+  ## Split code and paragraphs, find formatting
+  doc_struct = parse_m_source (doc_struct);
+
+  ## Create output directory
+  [mkdir_stat, mkdir_msg] = mkdir (options.outputDir);
+  if (mkdir_stat != 1)
+    error ("publish: cannot create output directory. %s", mkdir_msg);
+  endif
+
+  if (options.evalCode)
+    doc_struct = eval_code (doc_struct, options);
+  endif
+
+  output_file = create_output (doc_struct, options);
+endfunction
+
+
+
+function doc_struct = parse_m_source (doc_struct)
+  ## PARSE_M_SOURCE First parsing level
+  ##   This function extracts the overall structure (paragraphs and code
+  ##   sections) given in doc_struct.m_source.
+  ##
+  ##   The result is written to doc_struct.body, which then contains a cell
+  ##   vector of structs, either of
+  ##
+  ##     a) {struct ("type", "code", ...
+  ##                 "lines", [a, b], ...
+  ##                 "output", [])}
+  ##     b) {struct ("type", "section", ...
+  ##                 "content", title_str)}
+  ##
+  ##   Second parsing level is invoked for the paragraph contents, resulting
+  ##   in more elements for doc_struct.body.
+  ##
+
+  ## If there is nothing to parse
+  if (isempty (doc_struct.m_source))
+    return;
+  endif
+
+  ## Parsing helper functions
+  ##
+  ## Checks line to have N "%" or "#" lines
+  ## followed either by a space or is end of string
+  is_publish_markup = @(cstr, N) ...
+    any (strncmp (char (cstr), {"%%%", "##"}, N)) ...
+    && ((length (char (cstr)) == N) || ((char (cstr))(N + 1) == " "));
+  ## Checks line of cellstring to be a paragraph line
+  is_paragraph = @(cstr) is_publish_markup (cstr, 1);
+  ## Checks line of cellstring to be a section headline
+  is_head = @(cstr) is_publish_markup (cstr, 2);
+  ## Checks line of cellstring to be a headline without section break, using
+  ## the cell mode in Matlab (for compatibility), just treated as a new head.
+  is_no_break_head = @(cstr) is_publish_markup (cstr, 3);
+
+  ## Find the indices of paragraphs starting with "%%", "##", or "%%%"
+  par_start_idx = find ( ...
+    cellfun (is_head, doc_struct.m_source) ...
+    | cellfun (is_no_break_head, doc_struct.m_source));
+
+  ## If the whole document is code
+  if (isempty (par_start_idx))
+    doc_struct.body{end + 1}.type = "code";
+    doc_struct.body{end}.content = strtrim (strjoin (...
+      doc_struct.m_source(1:length(doc_struct.m_source)), "\n"));
+    doc_struct.body{end}.lines = [1, length(doc_struct.m_source)];
+    doc_struct.body{end}.output = {};
+    return;
+  endif
+
+  ## Determine continuous range of paragraphs
+  par_end_idx = [par_start_idx(2:end) - 1, length(doc_struct.m_source)];
+  for i = 1:length(par_end_idx)
+    idx = find (! cellfun (is_paragraph, ...
+                    doc_struct.m_source(par_start_idx(i) + 1:par_end_idx(i))));
+    if (! isempty (idx))
+      par_end_idx(i) = par_start_idx(i) + idx(1) - 1;
+    endif
+  endfor
+  ## Code sections between paragraphs
+  code_start_idx = par_end_idx(1:end - 1) + 1;
+  code_end_idx = par_start_idx(2:end) - 1;
+  ## Code at the beginning?
+  if (par_start_idx(1) > 1)
+    code_start_idx = [1, code_start_idx];
+    code_end_idx = [par_start_idx(1) - 1, code_end_idx];
+  endif
+  ## Code at the end?
+  if (par_end_idx(end) < length (doc_struct.m_source))
+    code_start_idx = [code_start_idx, par_end_idx(end) + 1];
+    code_end_idx = [code_end_idx, length(doc_struct.m_source)];
+  endif
+  ## Remove overlaps
+  idx = code_start_idx > code_end_idx;
+  code_start_idx(idx) = [];
+  code_end_idx(idx) = [];
+  ## Remove empty code blocks
+  idx = [];
+  for i = 1:length(code_start_idx)
+    if (all (cellfun (@(cstr) isempty (char (cstr)), ...
+                      doc_struct.m_source(code_start_idx(i):code_end_idx(i)))))
+      idx = [idx, i];
+    endif
+  endfor
+  code_start_idx(idx) = [];
+  code_end_idx(idx) = [];
+
+  ## Try to find a document title and introduction text
+  ##   1. First paragraph must start in first line
+  ##   2. Second paragraph must start before any code
+  title_offset = 0;
+  if ((is_head (doc_struct.m_source{1})) ...
+      && (! isempty (par_start_idx))
+      && (par_start_idx(1) == 1) ...
+      && ((isempty (code_start_idx))
+          || ((length (par_start_idx) > 1)
+              && (par_start_idx(2) < code_start_idx(1)))))
+    doc_struct.title = doc_struct.m_source{1};
+    doc_struct.title = doc_struct.title(4:end);
+    content = doc_struct.m_source(2:par_end_idx(1));
+    ## Strip leading "# "
+    content = cellfun(@(c) cellstr (c(3:end)), content);
+    doc_struct.intro = parse_paragraph_content (content);
+    title_offset = 1;
+  endif
+
+  ## Add non-empty paragraphs and code to doc_struct
+  j = 1;
+  i = (1 + title_offset);
+  while ((i <= length(par_start_idx)) || (j <= length(code_start_idx)))
+    ## Add code while there is code left
+    ##   and code is before the next paragraph or there are no more paragraphs
+    while ((j <= length(code_start_idx))
+           && ((i > length(par_start_idx))
+               || (par_start_idx(i) > code_start_idx(j))))
+      doc_struct.body{end + 1}.type = "code";
+      lines = [code_start_idx(j), code_end_idx(j)];
+      doc_struct.body{end}.content = strtrim (strjoin (...
+        doc_struct.m_source(lines(1):lines(2)), "\n"));
+      doc_struct.body{end}.lines = lines;
+      doc_struct.body{end}.output = {};
+      j++;
+    endwhile
+
+    if (i <= length(par_start_idx))
+      type_str = "section";
+      title_str = doc_struct.m_source{par_start_idx(i)};
+      if (is_head (doc_struct.m_source(par_start_idx(i))))
+        title_str = title_str(4:end);
+      else
+        title_str = title_str(5:end);
+      endif
+      ## Append, if paragraph title is given
+      if (! isempty (title_str))
+        doc_struct.body{end + 1}.type = type_str;
+        doc_struct.body{end}.content = title_str;
+      endif
+
+      content = doc_struct.m_source(par_start_idx(i) + 1:par_end_idx(i));
+      ## Strip leading "# "
+      content = cellfun(@(c) cellstr (c(3:end)), content);
+      doc_struct.body = [doc_struct.body, parse_paragraph_content(content)];
+      i++;
+    endif
+  endwhile
+endfunction
+
+
+
+function [p_content] = parse_paragraph_content (content)
+  ## PARSE_PARAGRAPH_CONTENT second parsing level
+  ##
+  ##   Parses the content of a paragraph (without potential title) and
+  ##   returns a cell vector of structs, that can be appended to
+  ##   doc_struct.body, either of
+  ##
+  ##     a) {struct ("type", "preformatted_code", ...
+  ##                 "content", code_str)}
+  ##     b) {struct ("type", "preformatted_text", ...
+  ##                 "content", text_str)}
+  ##     c) {struct ("type", "bulleted_list", ...
+  ##                 "content", {"item1", "item2", ..})}
+  ##     d) {struct ("type", "numbered_list", ...
+  ##                 "content", {"item1", "item2", ..})}
+  ##     e) {struct ("type", "include", ...
+  ##                 "content", file_str)}
+  ##     f) {struct ("type", "graphic", ...
+  ##                 "content", file_str)}
+  ##     g) {struct ("type", "html", ...
+  ##                 "content", html_str)}
+  ##     h) {struct ("type", "latex", ...
+  ##                 "content", latex_str)}
+  ##     i) {struct ("type", "text", ...
+  ##                 "content", text_str)}
+  ##
+  ##   Option i) might contain:
+  ##
+  ##     * Italic "_", bold "*", and monospaced "|" text
+  ##     * Inline "$" and block "$$" LaTeX math
+  ##     * Links
+  ##     * Trademark symbols
+  ##
+
+  p_content = cell ();
+
+  if (isempty (content))
+    return;
+  endif
+
+  ## Split into blocks seperated by empty lines
+  idx = [0, find(cellfun (@isempty, content)), length(content) + 1];
+  ## For each block
+  for i = find (diff(idx) > 1)
+    block = content(idx(i) + 1:idx(i+1) - 1);
+
+    ## Octave code (two leading spaces)
+    if (all (cellfun (@(c) strncmp (char (c), "  ", 2), block)))
+      p_content{end+1}.type = "preformatted_code";
+      block = cellfun(@(c) cellstr (c(3:end)), block);
+      p_content{end}.content = strjoin (block, "\n");
+      continue;
+    endif
+
+    ## Preformatted text (one leading space)
+    if (all (cellfun (@(c) strncmp (char (c), " ", 1), block)))
+      p_content{end+1}.type = "preformatted_text";
+      block = cellfun(@(c) cellstr (c(2:end)), block);
+      p_content{end}.content = strjoin (block, "\n");
+      continue;
+    endif
+
+    ## Bulleted list starts with "* "
+    if (strncmp (block{1}, "* ", 2))
+      p_content{end+1}.type = "bulleted_list";
+      p_content{end}.content = strjoin (block, "\n");
+      ## Revove first "* "
+      p_content{end}.content = p_content{end}.content(3:end);
+      ## Split items
+      p_content{end}.content = strsplit (p_content{end}.content, "\n* ");
+      continue;
+    endif
+
+    ## Numbered list starts with "# "
+    if (strncmp (block{1}, "# ", 2))
+      p_content{end+1}.type = "numbered_list";
+      p_content{end}.content = strjoin (block, "\n");
+      ## Revove first "# "
+      p_content{end}.content = p_content{end}.content(3:end);
+      ## Split items
+      p_content{end}.content = strsplit (p_content{end}.content, "\n# ");
+      continue;
+    endif
+
+    ## Include <include>fname.m</include>
+    if (! isempty ([~,~,~,~,fname] = regexpi (strjoin (block, ""), ...
+                                              '^<include>(.*)<\/include>$')))
+      ## Includes result in preformatted code
+      p_content{end+1}.type = "preformatted_code";
+      include_code = read_file_to_cellstr (strtrim ((fname{1}){1}));
+      p_content{end}.content = strjoin (include_code, "\n");
+      
+      continue;
+    endif
+    
+    ## Graphic <<myGraphic.png>>
+    if (! isempty ([~,~,~,~,fname] = regexpi (strjoin (block, ""), ...
+                                              '^<<(.*)>>$')))
+      p_content{end+1}.type = "graphic";
+      p_content{end}.content = strtrim ((fname{1}){1});
+      continue;
+    endif
+
+    ## Parse remaining blocks line by line
+    j = 1;
+    while (j <= length(block))
+      ## HTML markup
+      if (strcmpi (block{j}, "<html>"))
+        start_html = j + 1;
+        while ((j < length(block)) && ! strcmpi (block{j}, "</html>"))
+          j++;
+        endwhile
+        if ((j == length(block)) && ! strcmpi (block{j}, "</html>"))
+          warning ("publish: no closing </html> found");
+        else
+          j++;  ## Skip closing tag
+        endif
+        if (j > start_html)
+          p_content{end+1}.type = "html";
+          p_content{end}.content = strjoin (block(start_html:j-2), "\n");
+        endif
+      ## LaTeX markup
+      elseif (strcmpi (block{j}, "<latex>"))
+        start_latex = j + 1;
+        while ((j < length(block)) && ! strcmpi (block{j}, "</latex>"))
+          j++;
+        endwhile
+        if ((j == length(block)) && ! strcmpi (block{j}, "</latex>"))
+          warning ("publish: no closing </latex> found");
+        else
+          j++;  ## Skrip closing tag
+        endif
+        if (j > start_latex)
+          p_content{end+1}.type = "latex";
+          p_content{end}.content = strjoin (block(start_latex:j-2), "\n");
+        endif
+      ## Remaining normal text or markups belonging to normal text
+      ## that are handled while output generation:
+      ##
+      ## * Italic "_", bold "*", and monospaced "|" text
+      ## * Inline "$" and block "$$" LaTeX math
+      ## * Links
+      ## * Trademark symbols
+      ##
+      else
+        if ((j == 1) || isempty (p_content) ...
+            || ! strcmp (p_content{end}.type, "text"))
+          p_content{end+1}.type = "text";
+          p_content{end}.content = block{j};
+        else
+          p_content{end}.content = strjoin ({p_content{end}.content, ...
+                                             block{j}}, "\n");
+        endif
+        j++;
+      endif
+    endwhile
+  endfor
+endfunction
+
+
+
+function m_source = read_file_to_cellstr (file)
+  ## READ_FILE_TO_CELLSTR reads a given file line by line to a cellstring
+  fid = fopen (file, "r");
+  i = 1;
+  m_source{i} = fgetl (fid);
+  while (ischar (m_source{i}))
+    i++;
+    m_source{i} = fgetl (fid);
+  endwhile
+  fclose(fid);
+  m_source = cellstr (m_source(1:end-1)); ## No EOL
+endfunction
+
+
+
+function ofile = create_output (doc_struct, options)
+  ## CREATE_OUTPUT creates the desired output file
+  ##
+
+  formatter = [];
+  ofile_ext = "";
+  switch (options.format)
+    case "html"
+      formatter = @__publish_html_output__;
+      ofile_ext = ".html";
+    case {"latex", "pdf"}
+      formatter = @__publish_latex_output__;
+      ofile_ext = ".tex";
+  endswitch
+
+  ## Use title, or if not given the m-file name
+  title_str = doc_struct.title;
+  if (isempty (title_str))
+    [~,title_str] = fileparts (doc_struct.m_source_file_name);
+  endif
+
+  content = formatter ("header", title_str, ...
+    format_output (doc_struct.intro, formatter, options), ...
+    get_toc (doc_struct.body));
+  content = [content, format_output(doc_struct.body, formatter, options)];
+  content = [content, formatter("footer", strjoin (doc_struct.m_source, "\n"))];
+
+  ## Write file
+  [~,ofile] = fileparts (doc_struct.m_source_file_name);
+  ofile_name = [ofile, ofile_ext];
+  ofile = [options.outputDir, filesep(), ofile_name];
+  fid = fopen (ofile, "w");
+  fputs (fid, content);
+  fclose (fid);
+
+  ## Compile LaTeX, if compiler found
+  if (strcmp (options.format, "pdf"))
+    [status, ~] = system ("pdflatex --version");
+    if (status == 0)
+      for i = 1:2
+        system (["cd ", options.outputDir," && pdflatex ", ofile_name]);
+      endfor
+    endif
+  endif
+endfunction
+
+
+
+function toc_cstr = get_toc (cstr)
+  ## GET_TOC extracts the table of contents from a cellstring (e.g.
+  ##   doc_struct.body) with each section headline as a cell in a returned
+  ##   cellstring.
+  ##
+  toc_cstr = cell ();
+  for i = 1:length(cstr)
+    if (strcmp (cstr{i}.type, "section"))
+      toc_cstr{end + 1} = cstr{i}.content;
+    endif
+  endfor
+endfunction
+
+
+
+function str = format_output (cstr, formatter, options)
+  ## FORMAT_OUTPUT steps through all blocks (doc_struct.intro or
+  ##   doc_struct.body) in cstr and produces a single result string
+  ##   with the source code in the desired output format.
+  ##
+  ##   formatter has the only knowlegde how to enforce the target format
+  ##   and produces for each block the necessary target format source string.
+  ##
+
+  str = "";
+  for i = 1:length(cstr)
+    switch (cstr{i}.type)
+      case "code"
+        if (options.showCode)
+          str = [str, formatter(cstr{i}.type, cstr{i}.content)];
+        endif
+        if (options.evalCode)
+          str = [str, formatter("code_output", cstr{i}.output)];
+        endif
+      case "text"
+        str = [str, formatter(cstr{i}.type, ...
+          format_text (cstr{i}.content, formatter))];
+      case {"bulleted_list", "numbered_list"}
+        items = cellfun (@(str) format_text(str, formatter), ...
+          cstr{i}.content, "UniformOutput", false);
+        str = [str, formatter(cstr{i}.type, items)];
+      otherwise
+        str = [str, formatter(cstr{i}.type, cstr{i}.content)];
+    endswitch
+  endfor
+endfunction
+
+
+
+function str = format_text (str, formatter)
+  ## FORMAT_TEXT formats inline formats in strings.
+  ##   These are: links, bold, italic, monospaced, (TM), (R)
+  ##
+
+  ## Links "<http://www.someurl.com>"
+  str = regexprep (str, '<(\S{3,}[^\s<>]*)>', ...
+    formatter ("link", "$1", "$1"));
+  ## Links "<octave:Function TEXT>"
+  ## TODO: better pointer to the function documentation
+  str = regexprep (str, '<octave:([^\s<>]*) *([^<>$]*)>', ...
+    formatter ("link", ["https://www.gnu.org/software/octave/", ...
+      "doc/interpreter/Function-Index.html"], "$2"));
+  ## Links "<http://www.someurl.com TEXT>"
+  str = regexprep (str, '<(\S{3,}[^\s<>]*) *([^<>$]*)>', ...
+    formatter ("link", "$1", "$2"));
+  oldstr = str;
+  ## Loop because of inlined expressions, e.g. *BOLD _ITALIC_*
+  do
+    oldstr = str;
+    ## Bold
+    str = regexprep (str, '\*([^*$_|]*)\*', formatter ("bold", "$1"));
+    ## Italic
+    str = regexprep (str, '_([^_$|*]*)_', formatter ("italic", "$1"));
+    ## Monospaced
+    str = regexprep (str, '\|([^|$_*]*)\|', formatter ("monospaced", "$1"));
+  until (strcmp (str, oldstr))
+  ## Replace special symbols
+  str = strrep (str, "(TM)", formatter("TM"));
+  str = strrep (str, "(R)", formatter("R"));
+endfunction
+
+
+
+function doc_struct = eval_code (doc_struct, options)
+  ## EVAL_CODE Thrid level parsing
+  ##
+  ##   Generates the output of the script code and takes care of generated
+  ##   figures.
+  ##
+
+  ## Neccessary as the code does not run interactively
+  page_screen_output (0, "local");
+
+  ## Remember previously opened figures
+  fig_ids = findall (0, "type", "figure");
+  [~,fig_name] = fileparts (doc_struct.m_source_file_name);
+  fig_num = 1;
+  fig_list = struct ();
+
+  ## mat-file used as temporary context
+  tmp_context = [tempname(), ".mat"];
+
+  ## Evaluate code, that does not appear in the output.
+  eval_code_helper (tmp_context, options.codeToEvaluate);
+
+  ## Create a new figure, if there are existing plots
+  if (! isempty (fig_ids) && options.useNewFigure)
+    figure ();
+  endif
+
+  for i = 1:length(doc_struct.body)
+    if (strcmp (doc_struct.body{i}.type, "code"))
+      r = doc_struct.body{i}.lines;
+      code_str = strjoin (doc_struct.m_source(r(1):r(2)), "\n");
+      if (options.catchError)
+        try
+          doc_struct.body{i}.output = eval_code_helper (tmp_context, code_str);
+         catch err
+          doc_struct.body{i}.output = cellstr (["error: ", err.message, ...
+            "\n\tin:\n\n", code_str]);
+        end_try_catch
+      else
+        doc_struct.body{i}.output = eval_code_helper (tmp_context, code_str);
+      endif
+
+      ## Check for newly created figures ...
+      fig_ids_new = setdiff (findall (0, "type", "figure"), fig_ids);
+      ## ... and save them
+      for j = 1:length(fig_ids_new)
+        drawnow ();
+        if (isempty (get (fig_ids_new(j), "children")))
+          continue;
+        endif
+        fname = [fig_name, "-", num2str(fig_num), "."];
+        if (strncmp (options.imageFormat, "eps", 3))
+          fname = [fname, "eps"];
+        else
+          fname = [fname, options.imageFormat];
+        endif
+        print_opts = {fig_ids_new(j), ...
+          [options.outputDir, filesep(), fname], ...
+          ["-d", options.imageFormat], "-color"};
+        if (! isempty (options.maxWidth) && ! isempty (options.maxHeight))
+          print_opts{end + 1} = ["-S", num2str(options.maxWidth), ...
+            num2str(options.maxHeight)];
+        elseif (! isempty (options.maxWidth) || ! isempty (options.maxWidth))
+          warning (["publish: specify both options.maxWidth ", ...
+            "and options.maxWidth"]);
+        endif
+        print (print_opts{:});
+        fig_num++;
+        delete (fig_ids_new(j));
+        fig_elem = cell ();
+        fig_elem{1} = struct ("type", "graphic", "content", fname);
+        if (isfield (fig_list, num2str (i)))
+          fig_elem = [getfield(fig_list, num2str (i)), fig_elem];
+        endif
+        fig_list = setfield (fig_list, num2str (i), fig_elem);
+        ## Create a new figure, if there are existing plots
+        if (isempty (setdiff (findall (0, "type", "figure"), fig_ids)) ...
+            &&! isempty (fig_ids) && options.useNewFigure)
+          figure ();
+        endif
+      endfor
+
+      ## Truncate output to desired length
+      if (options.maxOutputLines < length (doc_struct.body{i}.output))
+        doc_struct.body{i}.output = ...
+          doc_struct.body{i}.output(1:options.maxOutputLines);
+      endif
+      doc_struct.body{i}.output = strjoin (doc_struct.body{i}.output, "\n");
+    endif
+  endfor
+
+  ## Close any by publish opened figures
+  delete (setdiff (findall (0, "type", "figure"), fig_ids));
+
+  ## Remove temporary context
+  unlink (tmp_context);
+
+  ## Insert figures to document
+  fig_code_blocks = fieldnames (fig_list);
+  body_offset = 0;
+  for i = 1:length(fig_code_blocks)
+    elems = getfield (fig_list, fig_code_blocks{i});
+    ## Compute index, where the figure(s) has to be inserterd
+    j = str2num (fig_code_blocks{i}) + body_offset;
+    doc_struct.body = [doc_struct.body(1:j), elems, doc_struct.body(j+1:end)];
+    body_offset = body_offset + length (elems);
+  endfor
+endfunction
+
+
+
+function ___cstr___ = eval_code_helper (___context___, ___code___);
+  ## EVAL_CODE_HELPER evaluates a given string with Octave code in an extra
+  ##   temporary context and returns a cellstring with the eval output
+
+  ## TODO: potential conflicting variables sourrounded by "___".  Maybe there
+  ##       is a better solution.
+  if (isempty (___code___))
+    return;
+  endif
+
+  if (exist (___context___, "file") == 2)
+    load (___context___);
+  endif
+
+  ___cstr___ = strsplit (evalc (___code___), "\n");
+
+  clear ___code___
+  save (___context___);
+endfunction
+
+
+
+## Bad function calls
+
+%!error publish ()
+%!error publish (1)
+%!error publish ("non_existing_file.m")
+%!error<Only Octave script files can be published> publish ("publish.m")
+%!error publish ("test_script.m", "format", "html", "showCode")
+%!error [str1, str2] = publish ("test_script.m")
--- a/scripts/help/__unimplemented__.m	Thu Jul 14 19:56:44 2016 -0400
+++ b/scripts/help/__unimplemented__.m	Fri Jul 15 11:46:16 2016 +0200
@@ -656,7 +656,6 @@
   "getpixelposition",
   "getReport",
   "gobjects",
-  "grabcode",
   "graph",
   "graymon",
   "griddedInterpolant",
@@ -785,7 +784,6 @@
   "properties",
   "propertyeditor",
   "psi",
-  "publish",
   "quad2d",
   "RandStream",
   "rbbox",
--- a/test/module.mk	Thu Jul 14 19:56:44 2016 -0400
+++ b/test/module.mk	Fri Jul 15 11:46:16 2016 +0200
@@ -54,6 +54,7 @@
 include test/ctor-vs-method/module.mk
 include test/fcn-handle-derived-resolution/module.mk
 include test/nest/module.mk
+include test/publish/module.mk
 
 ALL_LOCAL_TARGETS += test/.gdbinit
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/publish/module.mk	Fri Jul 15 11:46:16 2016 +0200
@@ -0,0 +1,8 @@
+publish_TEST_FILES = \
+  test/publish/test_script_code_only.m \
+  test/publish/test_script_empty.m \
+  test/publish/test_script_example.m \
+  test/publish/test_script_head_only.m \
+  test/publish/test_script.m
+
+TEST_FILES += $(publish_TEST_FILES)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/publish/publish.tst	Fri Jul 15 11:46:16 2016 +0200
@@ -0,0 +1,23 @@
+## publish
+
+%!testif HAVE_X_WINDOWS
+%! cases = dir ("test_script*.m");
+%! cases = strsplit (strrep ([cases.name], ".m", ".m\n"));
+%! for i = 1:length(cases)-1
+%!   publish (cases{i});
+%! endfor
+%! confirm_recursive_rmdir (false, "local");
+%! rmdir ("html", "s");
+
+## grabcode
+
+%!testif HAVE_X_WINDOWS
+%! publish ("test_script.m");
+%! str1 = fileread ("test_script.m");
+%! str2 = grabcode ("html/test_script.html");
+%! confirm_recursive_rmdir (false, "local");
+%! rmdir ("html", "s");
+%! # Canonicalize strings
+%! str1 = strjoin (deblank (strsplit (str1, "\n")), "\n");
+%! str2 = strjoin (deblank (strsplit (str2, "\n")), "\n");
+%! assert (hash ("md5", str1), hash ("md5", str2));
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/publish/test_script.m	Fri Jul 15 11:46:16 2016 +0200
@@ -0,0 +1,322 @@
+%% Headline
+% Headline description with a link
+% <https://www.gnu.org/software/octave |*GNU Octave*| Homepage>
+%
+% Spanning some lines and blanks.
+%
+
+%%
+
+disp ("First recognized Octave code after %%")
+
+%% SECTION TITLE
+% DESCRIPTIVE TEXT
+
+%%% SECTION TITLE WITHOUT SECTION BREAK
+% For Matlab compatibility
+
+## SECTION TITLE
+# DESCRIPTIVE TEXT
+
+### SECTION TITLE WITHOUT SECTION BREAK
+# Should not work in Octave style
+# and should be interpreted as usual Octave code
+
+%%
+%
+
+##
+#
+
+% some real comment
+i = 0:2*pi
+
+# some real comment
+y = sin(i)
+
+%%
+%
+% Content without head.
+%
+
+% some real comment and split code block
+x = 0:2*pi
+
+# some real comment and split code block
+y = sin(x)
+
+%%
+%
+
+% reusing old values
+y = cos(i)
+
+# some real comment and split code block
+y = cos(x)
+
+%% Text formatting
+% PLAIN TEXT _ITALIC TEXT_ *BOLD TEXT* |MONOSPACED TEXT|
+% |MONOSPACED TEXT| PLAIN TEXT _ITALIC TEXT_ *BOLD TEXT*
+% *BOLD TEXT* |MONOSPACED TEXT| PLAIN TEXT _ITALIC TEXT_
+% _ITALIC TEXT_ *BOLD TEXT* |MONOSPACED TEXT| PLAIN TEXT
+% Trademarks:
+% TEXT(TM)
+% TEXT(R)
+%
+% Good inlining shoud work
+% *BOLD _ITALIC |MONOSPACED| TEXT_*
+% _ITALIC *BOLD |MONOSPACED| TEXT*_
+% |MONOSPACED *BOLD _ITALIC_ TEXT*|
+%
+% Bad inlining should not work |MONOSPACED *BOLD TEXT|*
+
+% figure code
+plot (x,y)
+
+% another plot
+figure ()
+plot (y,x)
+
+## Text formatting
+# PLAIN TEXT _ITALIC TEXT_ *BOLD TEXT* |MONOSPACED TEXT|
+# |MONOSPACED TEXT| PLAIN TEXT _ITALIC TEXT_ *BOLD TEXT*
+# *BOLD TEXT* |MONOSPACED TEXT| PLAIN TEXT _ITALIC TEXT_
+# _ITALIC TEXT_ *BOLD TEXT* |MONOSPACED TEXT| PLAIN TEXT
+# Trademarks:
+# TEXT(TM)
+# TEXT(R)
+#
+# Good inlining shoud work
+# *BOLD _ITALIC |MONOSPACED| TEXT_*
+# _ITALIC *BOLD |MONOSPACED| TEXT*_
+# |MONOSPACED *BOLD _ITALIC_ TEXT*|
+#
+# Bad inlining should not work |MONOSPACED *BOLD TEXT|*
+
+% again another plot
+plot (x,y)
+
+%% Bulleted List
+%
+% * BULLETED ITEM 1
+% * BULLETED ITEM 2
+% * BULLETED ITEM 3 *BOLD*
+% * BULLETED ITEM 4 <http://www.someURL.com>
+%
+
+## Bulleted List
+#
+# * BULLETED ITEM 1
+# * BULLETED ITEM 2
+# * BULLETED ITEM 3 *BOLD*
+# * BULLETED ITEM 4 <http://www.someURL.com>
+#
+
+%% Numbered List
+%
+% # NUMBERED ITEM 1
+% # NUMBERED ITEM 2
+% # NUMBERED ITEM 3 *BOLD*
+% # NUMBERED ITEM 4 <http://www.someURL.com>
+%
+
+## Numbered List
+#
+# # NUMBERED ITEM 1
+# # NUMBERED ITEM 2
+# # NUMBERED ITEM 3 *BOLD*
+# # NUMBERED ITEM 4 <http://www.someURL.com>
+#
+
+%%
+%
+%  PREFORMATTED
+%  TEXT
+%
+
+##
+#
+#  PREFORMATTED
+#  TEXT
+#
+
+%% GNU Octave Code
+%
+%   for i = 1:10
+%     disp (x)
+%   endfor
+%
+
+## GNU Octave Code
+#
+#   for i = 1:10
+#     disp (x)
+#   endfor
+#
+
+%% External File Content
+%
+% <include>test_script_code_only.m</include>
+%
+
+## External File Content
+#
+# <include>test_script_code_only.m</include>
+#
+
+%% External Graphic
+%
+% <<test_script-1.png>>
+%
+
+## External Graphic
+#
+# <<test_script-1.png>>
+#
+
+%% Inline LaTeX
+% $f(n) = n^5 + 4n^2 + 2 |_{n=17}$
+
+## Inline LaTeX
+# $f(n) = n^5 + 4n^2 + 2 |_{n=17}$
+
+%% Block LaTeX
+% $$f(n) = n^5 + 4n^2 + 2 |_{n=17}$$
+
+## Block LaTeX
+# $$f(n) = n^5 + 4n^2 + 2 |_{n=17}$$
+
+%% Links
+% <https://www.gnu.org/software/octave>
+% <https://www.gnu.org/software/octave GNU Octave Homepage>
+% <octave:FUNCTION DISPLAYED TEXT>
+% <octave:FUNCTION Nested markup and newline
+% PLAIN TEXT(TM) _ITALIC TEXT_(R) *BOLD TEXT* |MONOSPACED TEXT|>
+% <https://www.gnu.org/software/octave Nested markup and newline
+% PLAIN TEXT(TM) _ITALIC TEXT_(R) *BOLD TEXT* |MONOSPACED TEXT|>
+%
+
+## Links
+# <https://www.gnu.org/software/octave>
+# <https://www.gnu.org/software/octave GNU Octave Homepage>
+# <octave:FUNCTION DISPLAYED TEXT>
+# <octave:FUNCTION Nested markup and newline
+# PLAIN TEXT(TM) _ITALIC TEXT_(R) *BOLD TEXT* |MONOSPACED TEXT|>
+# <https://www.gnu.org/software/octave Nested markup and newline
+# PLAIN TEXT(TM) _ITALIC TEXT_(R) *BOLD TEXT* |MONOSPACED TEXT|>
+#
+
+%% HTML Markup
+% <html>
+% <table><tr>
+% <td style="border: 1px solid black;">one</td>
+% <td style="border: 1px solid black;">two</td></tr></table>
+% </html>
+%
+
+## HTML Markup
+# <html>
+# <table>
+# <tr>
+# <td style="border: 1px solid black;">one</td>
+# <td style="border: 1px solid black;">two</td>
+# </tr>
+# </table>
+# </html>
+#
+
+%% LaTeX Markup
+% <latex>
+% \begin{equation}
+% \begin{pmatrix}
+% 1 & 2 \\ 3 & 4
+% \end{pmatrix}
+% \end{equation}
+% </latex>
+%
+
+## LaTeX Markup
+# <latex>
+# \begin{equation}
+# \begin{pmatrix}
+# 1 & 2 \\ 3 & 4
+# \end{pmatrix}
+# \end{equation}
+# </latex>
+#
+
+%% Long void
+%
+%
+%
+%
+%
+%
+%
+%
+% content
+%
+%
+%
+%
+%
+%
+%
+
+%%
+%
+%
+%
+%
+%
+%
+%
+%
+% and continued
+%
+%
+%
+%
+%
+%
+%
+
+## Long void
+#
+#
+#
+#
+#
+#
+#
+# content
+#
+#
+#
+#
+#
+#
+#
+#
+#
+#
+
+##
+#
+#
+#
+#
+#
+#
+#
+# and continued
+#
+#
+#
+#
+#
+#
+#
+#
+#
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/publish/test_script_code_only.m	Fri Jul 15 11:46:16 2016 +0200
@@ -0,0 +1,24 @@
+
+% Code only with a very very very very very very very very very very looong line
+%
+x = 5;
+
+for i = 1:5
+  x += i;  # Might be useful "perhaps"
+endfor
+
+%{
+Multiline comment with keyword if "if" and 'if'
+%}
+
+if (x == 'a')
+  y = sin (x);
+endif
+
+#{
+Multiline comment with keyword if "if" and 'if'
+#}
+
+str = "some % string \" ' with %{";
+str2 = 'another % string '' " with %{';
+%
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/publish/test_script_example.m	Fri Jul 15 11:46:16 2016 +0200
@@ -0,0 +1,16 @@
+%% Headline title
+%
+% Some *bold*, _italic_, or |monospaced| Text with
+% a <http://www.octave.org link to GNU Octave>.
+%%
+
+# "Real" Octave commands to be evaluated
+sombrero ()
+
+## Octave comment style supported as well
+#
+# * Bulleted list item 1
+# * Bulleted list item 2
+#
+# # Numbered list item 1
+# # Numbered list item 2
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/publish/test_script_head_only.m	Fri Jul 15 11:46:16 2016 +0200
@@ -0,0 +1,4 @@
+%% Headline
+% Headline description.
+% about some lines no blanks
+%%
\ No newline at end of file