2
$\begingroup$

I am writing some ODE notes with lots of pictures generated using Mathematica, and I'd like to include the code used to generate them.

Currently I have a notebook where all the pictures are generated. I have a doImage function that gets a filename, and a graphic object and exports it — nothing fancy at all.

doImage["sine-function",
  Plot[
    Sin[t], 
    {t, 0, 2 Pi}
    ]
]

Is there a way to have doImage also save the code it gets in the second argument? A plaintext rendition, say (it'd eventually get included in a LaTeX file, maybe after being run through pygments for prettyfication) I think one could make the function hold its arguments unevaluated and save them, but ideally the saved code would also keep the formatting! At least, the indentation, say.

I don't really understand why, so let me be explicit: what I am asking is how to get the function all above to produce a textfile whose contexts are

Plot[
  Sin[t], 
  {t, 0, 2 Pi}
  ]

Since I know how to save a string to a textfile, I would be equally please, of course, with learning how to produce a string which when serialized into a textfile would produce that.


FWIW, one can get an actual plaintext rendtion of a cell of code in a string with its original indentation — or a close approximation thereof — by saying something like

SelectionMove[PreviousCell[CellStyle -> {"Input"}], All, Cell];
FrontEndTokenExecute[SelectedNotebook[], "CopySpecial", "InputText"];
str = NotebookGet[ClipboardNotebook[]][[1, 1, 1]]

It even gets fractions, exponents and roots correct. The lines are wrapped (with correct indentation), but it'd be nice to control at what column, though.

$\endgroup$
13
  • $\begingroup$ Oh, wait. I read "a plaintext rendition" and thought that's what you wanted. Did you already achieve what my answer provides, and now you want to add the UI formatting on top of that? $\endgroup$
    – lericr
    Feb 13 at 20:36
  • $\begingroup$ @lericr, I actually need plain text (I want it to be copy/pastable, for example) $\endgroup$ Feb 13 at 21:09
  • 1
    $\begingroup$ Where do you want it to be saved??? $\endgroup$ Feb 13 at 21:18
  • 2
    $\begingroup$ @Mariano Suárez-Álvarez: Then I do not understand why you are asking "how to save code" (which has vague meaning in what you mean by saving) instead of "how to convert code into string". $\endgroup$ Feb 13 at 23:41
  • 2
    $\begingroup$ @MarianoSuárez-Álvarez You are falling squarely for the XY Problem fallacy: you think you figured out a partial solution to your problem and you are seeking help with your solution (generate a string from code), rather than with your actual problem (save and export code in plain text), without realizing that a better potential pathway may exist. That is the source of the frustration in some comments. $\endgroup$
    – MarcoB
    Feb 14 at 11:29

3 Answers 3

4
$\begingroup$

You can save the code in the comments of the file. I use ExportByteArray and ImportByteArray to demonstrate but you can convert this to Import and Export.

ClearAll[doImageCode];
SetAttributes[doImageCode, HoldFirst]
doImageCode[expr_, filename_, imageformat_ : "PNG"] :=
 ExportByteArray[ReleaseHold[expr], 
  imageformat, {"Comments" -> {"WLCode" -> ToString[Hold[expr]]}}]

Then

ba1 = doImageCode[Plot[Sin[t], {t, 0, 2  Pi}], "test"]

Mathematica graphics

Import image with

ImportByteArray[ba1, "PNG"]

Mathematica graphics

Import code with

ImportByteArray[ba1, {"PNG", "Comments", "WLCode"}]
"Hold[Plot[Sin[t], {t, 0, 2 Pi}]]"

You can then use ToExpression and ReleaseHold to access the code. Also, other software should be able to access this meta data as well.

Hope this helps.

$\endgroup$
3
  • $\begingroup$ While this is interesting, notice that I actually want a plain text, separate file. There is really not a lot of point in saving the code inside the image and then extracting the code from the image when I can save the code in a text file directly. $\endgroup$ Feb 13 at 23:54
  • $\begingroup$ Also, this will lose all formatting in the code. $\endgroup$ Feb 13 at 23:56
  • $\begingroup$ @MarianoSuárez-Álvarez You can simply export the Hold[expr] to a txt file in your code. I originally experimented with CodeFormat (e.g. CodeFormat[ToString[Hold[expr]]] ) but I could not get it to give sensible formatting. You can try with your txt export. $\endgroup$
    – Edmund
    Feb 14 at 12:20
2
$\begingroup$

Yes. The key is to use the Hold* attributes. I'll demonstrate with HoldRest.

SetAttributes[doImage, HoldRest];
doImage[title_, expression_] := Column[{title, ToString[Unevaluated[expression]], expression}];

doImage["sine-function", Plot[Sin[t], {t, 0, 2  Pi}]]

enter image description here

$\endgroup$
3
  • $\begingroup$ (This does something weird sometimes, btw. The image i.stack.imgur.com/of4Hw.png is the output cell, saved as a PNG, of the result of calling your function on some code. The random letters are numerators/denominators of fractions that appear in the code generating the image) $\endgroup$ Feb 14 at 0:27
  • $\begingroup$ I guess that code was serialized into three lines, which the frontend wrapped :-/ $\endgroup$ Feb 14 at 0:33
  • $\begingroup$ That's how ToString handles fractions. You could process the code in some other way, maybe InputForm. $\endgroup$
    – lericr
    Feb 14 at 0:56
0
$\begingroup$

This is the first cell group (code of which is to be saved as a string that preserve exact formatting of original cell):

enter image description here

Code of the input cell of the first cell group:

Plot[
     Sin[t],
     {t, 0, 2 Pi}
     ]

2^3 + 5^7 +
 8

Code of the second input cell (when executed it exports previous group cell as image and also saves the code of the first input cell into variable code as compressed string.

SelectionMove[PreviousCell[], All, CellGroup];

Export["C:\\CellGroup.png", 
 Notebook[{DeleteCases[NotebookRead[EvaluationNotebook[]], 
    CellLabel -> _, \[Infinity]]}], "PNG"]

code = Compress@
  ToString[DeleteCases[NotebookRead[SelectedCells[][[1]]], 
    CellLabel -> _], InputForm]

"C:\\CellGroup.png"

"1:eJx1UMtugzAQTP+E+tjSyi4stnuI1BJVyi0KufGQSIQSJAIoATUS4uP6Z/\
FaNRQ18WF3PDszfjxuq3Xw8zCb+VlRhJ/VZZE2aditq2+\
Fx05WRdUQ2yIhligqsVlqqf5PG+TlINWmmPSxavZ9L+n+brRJq6lBw+\
xNu7GschWrg3sDpuHm3Oj9y/lwJvjGrXVwguVX+\
qxw0NbZ6bw75XWDKgI458SMxzgizB2WZd3iA/BD/\
UNa7rNNfszO1svc6pxXyTxJPQCQHCRjT4m0R5pTCkAFd7nmB5pRTzLhuqDofuQFeEy6Ugi\
k4yusHnd/"

Then you can copy the compressed string or use variable code to print a cell of exactly the same formatting as you see in the notebook.

CellPrint@ToExpression@Uncompress@code

which will produce this:

enter image description here

$\endgroup$
3
  • $\begingroup$ This is certainly useful, as it very good way to embed the code in a usable way, thanks! But it does not result into something I can include in a figure in a latex document in a usable way. $\endgroup$ Feb 14 at 16:31
  • $\begingroup$ @Mariano Suárez-Álvarez: Copying code as plaintext from some latex file and pasting it into Mathematica can never fully preserve the original Mathematica formatting. $\endgroup$ Feb 14 at 20:20
  • $\begingroup$ There are degrees of loss. From ToString to the thing with the clipboard I added to the question there is a difference, say, and it is clear that no plaintext rendition will retain all the format, as the format includes colors, special characters, some 2D expressions, proportional spacing, and I guess other things. Your compressed cell approach is on one end of the spectrum, I'd say! But as I said, I found your answer useful. $\endgroup$ Feb 14 at 20:34

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