diff --git a/.gitignore b/.gitignore index dd2907c..9dd5f62 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,12 @@ test.jl -examples/*/*.png +examples/figures/ examples/*.md +examples/*.pdf examples/*.html tmp/ .idea *.*~ +*.aux +*.log +*.out diff --git a/examples/winston_sample.mdw b/examples/winston_sample.mdw index fa98a28..ee5fe55 100644 --- a/examples/winston_sample.mdw +++ b/examples/winston_sample.mdw @@ -33,7 +33,7 @@ p = plot(t, sinc(t)) You can also include a plot with caption and hide the code: -<>= +<>= p = plot(cumsum(randn(1000, 1))) xlabel("x") ylabel("sinc(x)") diff --git a/examples/winston_sample.pdf b/examples/winston_sample.pdf deleted file mode 100644 index 7dac669..0000000 Binary files a/examples/winston_sample.pdf and /dev/null differ diff --git a/src/JuliaReport.jl b/src/JuliaReport.jl index cf99534..c2a379a 100644 --- a/src/JuliaReport.jl +++ b/src/JuliaReport.jl @@ -1,6 +1,5 @@ module JuliaReport using Compat -import Base: display, writemime #Contains report global properties type Report <: Display @@ -10,15 +9,13 @@ type Report <: Display basename::String formatdict pending_code::String - figdir::String - executed::Array cur_result::String fignum::Int figures::Array cur_chunk::Dict function Report() - new("", false, "", "", Any[], "", "", Any[], "", 1, Any[], @compat Dict{Symbol, Any}()) + new("", false, "", "", Any[], "", "", 1, Any[], @compat Dict{Symbol, Any}()) end end @@ -29,7 +26,7 @@ const supported_mime_types = [MIME"image/png", MIME"text/plain"] -function display(doc::Report, data) +function Base.display(doc::Report, data) for m in supported_mime_types if mimewritable(m(), data) display(doc, m(), data) @@ -48,23 +45,25 @@ module ReportSandBox end -function weave(source ; doctype = "pandoc", plotlib="PyPlot", informat="noweb", figdir = "figures", figformat = nothing) +function weave(source ; doctype = "pandoc", plotlib="PyPlot", informat="noweb", fig_path = "figures", fig_ext = nothing) cwd, fname = splitdir(abspath(source)) basename = splitext(fname)[1] formatdict = formats[doctype].formatdict - figformat == nothing || (formatdict[:figfmt] = figformat) + fig_ext == nothing || (rcParams[:chunk_defaults][:fig_ext] = fig_ext) + + #report = Report(source, false, cwd, basename, formatdict, "", figdir) report.source = source report.cwd = cwd report.basename = basename - report.figdir = figdir + rcParams[:chunk_defaults][:fig_path] = fig_path report.formatdict = formatdict if plotlib == nothing - rcParams[:chunk][:defaultoptions][:fig] = false + rcParams[:chunk_defaults][:fig] = false else l_plotlib = lowercase(plotlib) if l_plotlib == "winston" @@ -76,6 +75,10 @@ function weave(source ; doctype = "pandoc", plotlib="PyPlot", informat="noweb", elseif l_plotlib == "gadfly" eval(parse("""include(Pkg.dir("JuliaReport","src","gadfly.jl"))""")) rcParams[:plotlib] = "Gadfly" + if rcParams[:chunk_defaults][:fig_ext] != ".png" + rcParams[:chunk_defaults][:fig_ext] = ".png" + warn("Saving figures as .png with Gadfly") + end end end @@ -152,19 +155,19 @@ function run(parsed) if chunk[:type] == "code" #print(chunk["content"]) info("Weaving chunk $(chunk[:number]) from line $(chunk[:start_line])") - defaults = copy(rcParams[:chunk][:defaultoptions]) + defaults = copy(rcParams[:chunk_defaults]) options = copy(chunk[:options]) try - options = merge(rcParams[:chunk][:defaultoptions], options) + options = merge(rcParams[:chunk_defaults], options) catch - options = rcParams[:chunk][:defaultoptions] + options = rcParams[:chunk_defaults] warn("Invalid format for chunk options line: $(chunk[:start_line])") end merge!(chunk, options) delete!(chunk, :options) - chunk[:evaluate] || (chunk[:result] = ""; continue) #Do nothing if eval is false + chunk[:eval] || (chunk[:result] = ""; continue) #Do nothing if eval is false report.fignum = 1 report.cur_result = "" @@ -197,15 +200,14 @@ end function savefigs_pyplot(chunk) fignames = String[] - ext = report.formatdict[:figfmt] - figpath = joinpath(report.cwd, report.figdir) + ext = report.formatdict[:fig_ext] + figpath = joinpath(report.cwd, chunk[:fig_path]) isdir(figpath) || mkdir(figpath) chunkid = (chunk[:name] == nothing) ? chunk[:number] : chunk[:name] #Iterate over all open figures, save them and store names for fig = plt.get_fignums() - full_name = joinpath(report.cwd, report.figdir, "$(report.basename)_$(chunkid)_$fig$ext") - rel_name = "$(report.figdir)/$(report.basename)_$(chunkid)_$fig$ext" #Relative path is used in output - savefig(full_name) + full_name, rel_name = get_figname(report, chunk, fignum=fig) + savefig(full_name, dpi=chunk[:dpi]) push!(fignames, rel_name) plt.draw() plt.close() @@ -215,22 +217,20 @@ end -function display(report::Report, m::MIME"text/plain", data) +function Base.display(report::Report, m::MIME"text/plain", data) s = reprmime(m, data) report.cur_result *= s - push!(report.executed, s) end -function get_figname(report::Report, chunk) - figpath = joinpath(report.cwd, report.figdir) +function get_figname(report::Report, chunk; fignum = nothing) + figpath = joinpath(report.cwd, chunk[:fig_path]) isdir(figpath) || mkdir(figpath) - ext = report.formatdict[:figfmt] - fig = report.fignum - chunk = report.cur_chunk - chunkid = (chunk[:name] == nothing) ? chunk[:number] : chunk[:name] - full_name = joinpath(report.cwd, report.figdir, "$(report.basename)_$(chunkid)_$fig$ext") - rel_name = "$(report.figdir)/$(report.basename)_$(chunkid)_$fig$ext" #Relative path is used in output + ext = chunk[:fig_ext] + fignum == nothing && (fignum = report.fignum) + chunkid = (chunk[:name] == nothing) ? chunk[:number] : chunk[:name] + full_name = joinpath(report.cwd, chunk[:fig_path], "$(report.basename)_$(chunkid)_$(fignum)$ext") + rel_name = "$(chunk[:fig_path])/$(report.basename)_$(chunkid)_$(fignum)$ext" #Relative path is used in output return full_name, rel_name end diff --git a/src/config.jl b/src/config.jl index 79c0ba8..d9992dc 100644 --- a/src/config.jl +++ b/src/config.jl @@ -1,25 +1,70 @@ const rcParams = - @compat Dict{Symbol,Any}(:figdir=> "figures", + @compat Dict{Symbol,Any}( :plotlib => "PyPlot", - :storeresults=> false, - :cachedir=> "cache", - :chunk=> - Dict{Symbol,Any}(:defaultoptions=> - Dict{Symbol,Any}(:echo=> true, - :results=> "verbatim", + :storeresults => false, + :chunk_defaults => Dict{Symbol,Any}(:echo=> true, + :results=> "markup", :fig=> true, :include=> true, - :evaluate=> true, - :caption=> false, + :eval => true, + :fig_cap=> false, + :fig_width => 8, #Size in inches + :fig_height => 6, + :fig_path=> "figures", + :out_width=> nothing, #Defined separately for each format + :out_height=> nothing, + :fig_ext => ".png", + :dpi => 200, :term=> false, :name=> nothing, :wrap=> true, - :f_pos=> "htpb", - :f_size=> (8, 6), - :f_env=> nothing, - :f_spines=> true, - :complete=> true, + :fig_pos=> nothing, + :fig_env=> nothing, :engine=> "julia", :option_string=> "") ) - ) + + + + +# Working towards Knitr compatible options, implemented options are added to defaultoptions dictionary above +# and work in progress stays here, options from https://github.com/yihui/knitr/blob/master/R/defaults.R +# If you need a particular options, consider implementing it and making a pull request. + +#tidy = FALSE, +#tidy.opts = NULL, +#collapse = FALSE +#prompt = FALSE +#highlight = TRUE +#strip.white = TRUE +#size = 'normalsize' +#background = '#F7F7F7', +#cache = FALSE +#cache.path = 'cache/' +#cache.vars = NULL +#cache.lazy = TRUE, +#dependson = NULL +#autodep = FALSE, +#fig.keep = 'high' +#fig.show = 'asis' +#fig.align = 'default' +#dev = NULL +#dev.args = NULL +#fig.ext = NULL +#fig.scap = NULL +#fig.lp = 'fig:' +#fig.subcap = NULL, +#out.extra = NULL +#fig.retina = 1, +#external = TRUE +#sanitize = FALSE +#interval = 1 +#aniopts = 'controls,loop', +#warning = TRUE +#error = TRUE +#message = TRUE, +#render = NULL, +#ref.label = NULL +#child = NULL +#split = FALSE +#purl = TRUE diff --git a/src/formatters.jl b/src/formatters.jl index 6194fd1..b9d0b41 100644 --- a/src/formatters.jl +++ b/src/formatters.jl @@ -5,15 +5,27 @@ function format(executed, doctype) formatted = String[] docformat = formats[doctype] #@show docformat + + #Complete format dictionaries with defaults formatdict = docformat.formatdict get!(formatdict, :termstart, formatdict[:codestart]) get!(formatdict, :termend, formatdict[:codeend]) + get!(formatdict, :out_width, nothing) + get!(formatdict, :out_height, nothing) + get!(formatdict, :fig_pos, nothing) + get!(formatdict, :fig_env, nothing) + for chunk in copy(executed) if chunk[:type] == "doc" push!(formatted, chunk[:content]) else + #Fill undefined options with format specific defaults + chunk[:out_width] == nothing && (chunk[:out_width] = docformat.formatdict[:out_width]) + chunk[:fig_env] == nothing && (chunk[:fig_env] = docformat.formatdict[:fig_env]) + chunk[:fig_pos] == nothing && (chunk[:fig_pos] = docformat.formatdict[:fig_pos]) + #Format code result = format_codechunk(chunk, formatdict) @@ -34,7 +46,7 @@ end function format_codechunk(chunk, formatdict) - if !chunk[:evaluate] + if !chunk[:eval] if chunk[:echo] result = "$(formatdict[:codestart])$(chunk[:content])$(formatdict[:codeend])" return result @@ -59,10 +71,10 @@ function format_codechunk(chunk, formatdict) if (strip(chunk[:result])!= "") && (chunk[:results] != "hidden") #@show chunk - if chunk[:results] != "verbatim" + if chunk[:results] != "markup" haskey(formatdict, :indent) && (chunk[:result] = indent(chunk[:result])) result *= "$(chunk[:result])" - elseif chunk[:results] == "verbatim" + elseif chunk[:results] == "markup" result *= "$(formatdict[:outputstart])$(chunk[:result])\n$(formatdict[:outputend])\n" end end @@ -98,9 +110,11 @@ const tex = Tex(@compat Dict{Symbol,Any}(:codestart => "\\begin{juliacode}", :codeend => "\\end{juliacode}", :outputstart => "\\begin{juliaout}", :outputend => "\\end{juliaout}", - :figfmt => ".pdf", + :fig_ext => ".pdf", :extension =>"tex", - :width => "\\linewidth", + :out_width=> "\\linewidth", + :fig_env=> "figure", + :fig_pos => "htpb", :doctype => "tex" )) @@ -110,9 +124,11 @@ const texminted = Tex(@compat Dict{Symbol,Any}(:codestart => "\\begin{minted}[ma :outputend => "\\end{minted}", :termstart=> "\\begin{minted}[fontsize=\\footnotesize, xleftmargin=0.5em, mathescape]{julia}", :termend => "\\end{minted}", - :figfmt => ".pdf", + :fig_ext => ".pdf", :extension =>"tex", - :width => "\\linewidth", + :out_width => "\\linewidth", + :fig_env=> "figure", + :fig_pos => "htpb", :doctype => "texminted" )) @@ -124,19 +140,20 @@ const pandoc = Pandoc(@compat Dict{Symbol,Any}(:codestart => "~~~~{.julia}", :codeend=>"~~~~~~~~~~~~~\n\n", :outputstart=>"~~~~{.julia}", :outputend=>"~~~~~~~~~~~~~\n\n", - :figfmt=>".png", + :fig_ext=>".png", :extension=>"md", - :width=>"15 cm", :doctype=>"pandoc" )) function formatfigures(chunk, docformat::Tex) fignames = chunk[:figure] - caption = chunk[:caption] - width = get!(chunk, :width, docformat.formatdict[:width]) - f_pos = chunk[:f_pos] - f_env = chunk[:f_env] + caption = chunk[:fig_cap] + width = chunk[:out_width] + + + f_pos = chunk[:fig_pos] + f_env = chunk[:fig_env] result = "" figstring = "" @@ -144,6 +161,7 @@ function formatfigures(chunk, docformat::Tex) result *= """\\begin{$f_env}\n""" end + for fig = fignames figstring *= "\\includegraphics[width= $width]{$fig}\n" end @@ -172,8 +190,7 @@ end function formatfigures(chunk, docformat::Pandoc) fignames = chunk[:figure] - caption = chunk[:caption] - width = get!(chunk, :width, docformat.formatdict[:width]) + caption = chunk[:fig_cap] result = "" figstring = "" diff --git a/src/gadfly.jl b/src/gadfly.jl index 4fefc06..594a736 100644 --- a/src/gadfly.jl +++ b/src/gadfly.jl @@ -3,8 +3,14 @@ using Gadfly Gadfly.set_default_plot_format(:png) #Captures figures -function display(report::Report, m::MIME"image/png", data) +function Base.display(report::Report, m::MIME"image/png", p::Plot) chunk = report.cur_chunk + + if chunk[:fig_ext] != ".png" + chunk[:fig_ext] + warn("Saving figures as .png with Gadfly") + end + full_name, rel_name = get_figname(report, chunk) docformat = formats[report.formatdict[:doctype]] @@ -21,7 +27,11 @@ function display(report::Report, m::MIME"image/png", data) end report.fignum += 1 - out = open(full_name, "w") - writemime(out, m, data) - close(out) + + #TODO other formats + r = chunk[:dpi]/96 #Relative to Gadfly default 96dpi + draw(PNG(full_name, chunk[:fig_width]inch*r, chunk[:fig_height]inch*r ), p) + #out = open(full_name, "w") + #writemime(out, m, data) + #close(out) end diff --git a/src/winston.jl b/src/winston.jl index 103a2dc..c48ff56 100644 --- a/src/winston.jl +++ b/src/winston.jl @@ -1,6 +1,6 @@ using Winston -function display(report::Report, m::MIME"image/png", data) +function Base.display(report::Report, m::MIME"image/png", data) chunk = report.cur_chunk full_name, rel_name = get_figname(report, chunk) @@ -19,7 +19,7 @@ function display(report::Report, m::MIME"image/png", data) end #TODO get width and height from chunk options, after implementing Knitr compatible options - savefig(data, full_name, width=2000, height=800) + savefig(data, full_name, width=chunk[:fig_width]*chunk[:dpi], height=chunk[:fig_height]*chunk[:dpi]) report.fignum += 1 #out = open(full_name, "w") #writemime(out, m, data)