1
$\begingroup$

I have just discovered the free (as in free beer) Wolfram Engine, coming with a generous licence. After playing with Mathematica for a while, and missing the notebook feature, I would like to generate a PDF out of the .wls scripts I am going to create.

In order to do this, I assume I have to create a notebook and subsequently exporting it to a PDF.

Consider the following foo.wls script:

(* = Title = *)

1 + 2 
{x + y, 1/x + 1/y}

(* = Plots = *)
Plot[x^2, {x, -10, 10}]

I can generate the PDF like follows:

(* Options suggested by https://mathematica.stackexchange.com/a/133058/95308 *)
SetOptions[First[$Output], FormatType -> StandardForm]; 

(* Import script as a list of deferred expressions *)
wls = Import[FileNameJoin[{$TemporaryDirectory, "foo.wls"}], "HeldExpressions"];
exprs = wls /. HoldComplete -> Defer;

(* Convert expressions into notebook cells *)
cells = Function[e, ExpressionCell[e, "Input"]] /@  exprs; 

(* This is for the eyes *)
cells = Join[{TextCell["Testing .wls Scripts", "Title"]}, cells];

UsingFrontEnd[

  (* Make the cells into an in-memory notebook *)
  nb = CreateDocument[cells];
  
  (* Evaluate input cells to add the related output *)
  NotebookEvaluate[nb, InsertResults -> True, EvaluationElements -> All];
  
  (* Save the notebook to a file and export it as a PDF *)
  NotebookSave[nb, FileNameJoin[{$TemporaryDirectory, "foo.nb"}]];
  Export[FileNameJoin[{$TemporaryDirectory, "foo.pdf"}], nb];
  NotebookClose[nb]
  
]

The result is not particularly bad, as per the picture below, but comments are gone.

Generated PDF

This is due to Import[...foo.wls", "HeldExpressions"] above. I could use "Comments", instead, and get them. Still, I would not get their position with respect to expressions. Manually parsing the file would be possible only for expressions spanning over a single line, as there is no simple way to tell in which line a multiline expression ends.

What are your suggestions?

$\endgroup$
5
  • $\begingroup$ This doesn't directly address your question as you seem to be interested in .pdf export of nb expressions including comments. But sometimes people are only interested in the plots / graphics generated by M and don't need to see the code, such as, for example, in financial trading applications. You could put pertinent data in the PlotLabel with a Style expression. Then create new Java windows that pop up, as I have explained in youtube.com/watch?v=S1maEG-0nvE. Now you can move them anywhere you want on your screens, and also update content in existing windows programmatically. FWIW. $\endgroup$ Commented Dec 2, 2023 at 7:12
  • $\begingroup$ @AndreasLauschke: Thank you, but the issue here is not the plot, rather symbolic calculus. Comments are necessaries to explain manipulations. Basically, I am trying to create a poor man's notebook to share equations with other people. The parser can produce separately both a list of expressions and a list of comments, there should be a way to get a single list for the whole document. $\endgroup$
    – antonio
    Commented Dec 2, 2023 at 16:51
  • $\begingroup$ if your goal is to create a "poor man's notebook to share (content) with other people", you could consider the online version of M, or the python / jupyter version of providing a M-like front-end. Online M is free, and the python / jupyter stuff is too. I won't touch python / jupyter even if I had a gun to my head, but you will find plenty of resources here on mse and at other places on the web how to do that. It's apparently a streamlined process now to incorporate the required libraries. If you just want to share M content, that is easy and free these days. $\endgroup$ Commented Dec 3, 2023 at 3:07
  • $\begingroup$ @AndreasLauschke: Typing a long document in the cloud is inconvenient and, for a short one, I end up using pen & paper. Specifically, when I move the cursor up/down, the document does not scroll accordingly, also there are no key shortcuts. So I am using Wolfram Script inside Emacs, where I can send individual lines or regions to the Wolfram console, un/comment fast, etc. I just need to perfect the PDF export. The official Mathematica plugin for Jupyter seems just a Wolfram Cloud that works, but exported PDFs look weird. $\endgroup$
    – antonio
    Commented Dec 3, 2023 at 23:59
  • $\begingroup$ an indirectly way: use jerryi.github.io/wljs-docs , then export html to pdf. $\endgroup$ Commented Dec 4, 2023 at 16:14

3 Answers 3

1
$\begingroup$
  1. Load the script as text.
  2. Replace all (* ... *) with cOmMeNt["..."], eventually with proper escaping.
  3. Put text into a stream using StringToStream.
  4. Import that.
  5. Comments are available as expressions with the cOmMeNt head. The weird spelling is to make name clashes unlikely.
  6. Wrap TextCell cells around the contents of the cOmMeNt expressions.
  7. Generate the notebook as previously.
$\endgroup$
1
  • $\begingroup$ Thank you, but I probably found a less hacky solution. $\endgroup$
    – antonio
    Commented Dec 4, 2023 at 6:31
1
$\begingroup$

another method, use player :) :

take your code as an example:

comment can be TextCell with style.

Column[{
  (* = Title = *)
TextCell["Testing .wls Scripts", "Title"],
TextCell["1+2"],
1 + 2 ,
TextCell["{x + y, 1/x + 1/y}"],
{x + y, 1/x + 1/y},

(* = Plots = *)
TextCell["Plot[x^2, {x, -10, 10}],"],
Plot[x^2, {x, -10, 10}],

}]

enter image description here

You can use wolfram engine + Cell@BoxData@ToBoxes to create this cdf file.

My tool is https://github.com/asukaminato0721/mmaplayer/blob/master/server.wls

$\endgroup$
2
  • $\begingroup$ Does your tool work only with VSCode? $\endgroup$
    – antonio
    Commented Dec 5, 2023 at 3:34
  • $\begingroup$ @antonio the main logic is only in wls, that vscode plugin just launch the server, send the text to wls server, shut down the server. In theory, you can use any editor, like vim, neovim, emacs, etc. $\endgroup$ Commented Dec 5, 2023 at 5:59
0
$\begingroup$

I found a solution using the low-level cell representation Cell[].

Using Jupyter front-end

This code works using the free (as in free-speech) Jupyter front-end with the Wolfram Mathematica plugin.

(* Options suggested by https://mathematica.stackexchange.com/a/133058/95308 *)
SetOptions[First[$Output], FormatType -> StandardForm]; 

(* Import script as a text string *)
text = Import[FileNameJoin[{$TemporaryDirectory, "foo.wls"}], "Text"];

UsingFrontEnd[

  (* Make the cells into an in-memory notebook *)
  nb = CreateDocument[{  
    (* This is for the eyes *)
    TextCell["Testing .wls Scripts", "Title"],
    Cell[text, "Input"]
    }];
      
  (* Evaluate input cells to add the related output *)
  NotebookEvaluate[nb, InsertResults -> True, EvaluationElements -> All];
  
  (* Save the notebook to a file and export it as a PDF *)
  NotebookSave[nb, FileNameJoin[{$TemporaryDirectory, "foo.nb"}]];
  Export[FileNameJoin[{$TemporaryDirectory, "foo.pdf"}], nb];
  NotebookClose[nb]
  
]

Here is the resulting PDF:
Generated PDF

Using wolframscript (UPDATED)

When converted into an executable script, the code above presents a few issues. Essentially due to the fact that wolframscript exits too fast, before the evaluation is fully done. To fix this, I refactored the code, in order to wait for evaluation to be completed.


(* Check if the notebook is still evaluating *)
notebookEvaluatingQ[nb_] := Length[notebookEvaluatingCells[nb]] > 0

(* Get the currently evaluating cells in the notebook *)
notebookEvaluatingCells[nb_] := Select[Cells[nb], "Evaluating" /. Developer`CellInformation[#]&]

(* Pause until evaluation is finished *)
notebookPauseForEvaluation[nb_] := Module[{},  
  While[notebookEvaluatingQ[nb],
       Print["..."]
       Pause[0.25]]
]


(* Import script as a text string *)
text = Import[FileNameJoin[{$TemporaryDirectory, "foo.wls"}], "Text"];

UsingFrontEnd[

  (* Make the cells into an in-memory notebook *)
  nb = CreateDocument[{  
    (* This is for the eyes *)
    TextCell["Testing .wls Scripts", "Title"],
    Cell[text, "Input"]
    }];
  
  (* Select all cells and evaluate them *)
  SelectionMove[nb, All, Notebook]; 
  SelectionEvaluate[nb]; 

  (* Function to check if the notebook is still evaluating *)
  notebookPauseForEvaluation[nb]
    
  (* Save the notebook to a file and export it as a PDF *)
  NotebookSave[nb, FileNameJoin[{$TemporaryDirectory, "foo.nb"}]];
  Export[FileNameJoin[{$TemporaryDirectory, "foo.pdf"}], nb];
  NotebookClose[nb]
 
]


If you save the code as export.wls, you can execute it with:

wolframscript -f export.wls

In Linux, you can also make it executable by adding the recommended shebang in the first line:

#!/usr/bin/env wolframscript

And adding the executable bit, perhaps with:

chmod  +x ./export.wls
$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.