4

Update Mr Wizard's answer gives pixel-perfect results, but it is Windows-only and destroys the clipboard contents. My answer should work on any platform, but it's less precise: e.g. it omits In/Out labels. It does allow setting the rasterization width though.


This problem came up when I was trying to make a preview window for an image uploader (see the end of that answer).

I would like to create a palette button that will upload the current notebook selection as an image. Before uploading, I would like to show a preview of the image, to reduce the chance of something going awry before contacting the server.

This is what I have so far (includes only the preview code, not the uploader):

button = Button[
  "Preview",
  Module[
    {expr = NotebookRead@InputNotebook[]}, 
    If[expr =!= {},
      With[{img = Rasterize[expr]},
        MessageDialog[
          Column[{"Would you like to perform the action?", img}], 
          {"Do it!" :> doIt[img], "Cancel" :> Null}
        ]
      ]
    ]
  ]
]

In case you are wondering why I used a nested With inside the Module instead of making img a module-variable too: it's because by the time doIt[img] is evaluated, the local module variables will have been cleared, so I need to substitute the rasterized expression directly into the doIt function,

This button works (more or less). You can try it by creating a graphic in the same notebook (e.g. Graphics[Circle[]]), selecting it using a single click, then clicking the Preview button.

However, if I put it in a palette using CreatePalette[button], then the rasterization will happen for the window-width of the palette, and I get something like this:

Screenshot of the problem

How can I control the width of rasterization, or more generally, how can I create a preview dialog for the uploader that avoids this issue?

For an additional improvement, it would be nice to be able to size the message window so it fits the preview image (and still shows the button: the button disappears with WindowSize -> All).


Answers

Mr. Wizard's suggestion:

button = Button[
  "Preview", (FrontEndExecute[
    FrontEndToken[FrontEnd`SelectedNotebook[], "CopySpecial", "MGF"]];
    MessageDialog[
    First@Cases[NotebookGet@ClipboardNotebook[], 
      RasterBox[data_, ___] :> 
       Image[data, "Byte", ColorSpace -> "RGB", Magnification -> 1], 
      Infinity]])]

CreatePalette[button]

Problems: It (probably) only works on Windows, and it destroys the clipboard contents.

Community
  • 1
  • 1
Szabolcs
  • 24,728
  • 9
  • 85
  • 174
  • I tried extracting the `WindowSize` from the `InputNotebook[]` and passing that through to the `ImageSize` or `RasterSize` options of `Rasterize`. But I couldn't find the magic construction (I don't think that `ImageSize->{{wmax},{hmax}}` works quite like it claims in the documentation). Have you tried something like this? – Simon Dec 02 '11 at 10:57
  • @Simon I tried, but didn't manage with `ImageSize`/`RasterSize` – Szabolcs Dec 02 '11 at 10:59
  • Is the preview of major importance, or would getting the save itself working be a reasonable answer? – Mr.Wizard Dec 02 '11 at 23:07
  • @Mr.Wizard The whole point of the question is to get the preview. To catch potential errors before [upload](http://stackoverflow.com/questions/8038288/upload-images-to-imgur-from-mathematica). – Szabolcs Dec 03 '11 at 09:50
  • @Mr.Wizard You found a way to save it to a file? BTW for my own personal use I think I'll be using your solution. `Rasterize[]` doesn't always give me exactly what I see in the notebook (elements are shifted). – Szabolcs Dec 03 '11 at 10:09
  • I thought I was making progress, but no, I did not. – Mr.Wizard Dec 04 '11 at 07:45

4 Answers4

2

If it is practical to use the clipboard in this operation you might use: FrontEnd`CopySpecial["MGF"] (copy as bitmap).

Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
  • Let's have a more complete example, as it's not clear from your answer how this would work: ``CreatePalette@Button["Copy", FrontEndExecute[ FrontEndToken[FrontEnd`SelectedNotebook[], "CopySpecial", "MGF"]]]`` My aim is to get the current selection of a notebook previewed in a window (before uploading it as an image). If I understand you right, you are suggesting using the clipboard for transferring the selection to the preview dialogue ... – Szabolcs Dec 02 '11 at 09:31
  • ... This has a couple of problems: 1. It will destroy the clipboard content even if I cancel the dialogue. 2. "MGF" only works on Windows according to the docs. Otherwise it works quite fine. – Szabolcs Dec 02 '11 at 09:31
  • @Szabolcs I wrote that right before bed, and I didn't work anything out. I'll take another look at this whole problem today. Also I forgot to +1 the question but I just corrected that. – Mr.Wizard Dec 02 '11 at 18:45
  • Wow, what time zone are you in? – Szabolcs Dec 02 '11 at 22:37
  • @Szabolcs, evidence indicates Martian. – Mr.Wizard Dec 02 '11 at 23:05
  • That explains a few things :) – Dr. belisarius Dec 04 '11 at 16:03
  • 1
    Although this is Windows-only, and destroys the clipboard even if I cancel the operation, it works great for personal use. Now I have a single-button StackOverflow image uploader on a Mathematica palette :-) – Szabolcs Dec 14 '11 at 13:45
  • @Szabolcs, Mac OS has a clipboard too doesn't it? Why would your code break? – Mr.Wizard Dec 14 '11 at 15:17
  • @Mr.Wizard I don't *know* that it breaks, but [the docs say](http://reference.wolfram.com/mathematica/ref/frontendobject/CopySpecial.html) that "MGF" (i.e. copying in bitmap format) is Windows-only. – Szabolcs Dec 14 '11 at 15:59
  • @Szabolcs that page (which I should have bothered to read before making baseless statements) seems to indicate that there is not a `Copy As (raster image format)` option on other systems. Can that be true? – Mr.Wizard Dec 14 '11 at 16:08
1

I managed to do this by copying the selection to a new notebook, rasterizing the full notebook, then closing it.

CreatePalette@Button["Preview",
  Module[{target},
   target = 
    CreateDocument[{}, WindowSelected -> False, Visible -> False];
   NotebookWrite[target, NotebookRead[SelectedNotebook[]]];
   CreateDialog[{Rasterize[target], DefaultButton[]}];
   NotebookClose[target]
   ]
  ]

The WindowSize -> 500 option can be added to CreateDocument to set the rasterization width to 500 pixels.


Note some disadvantages (advantages in some cases) of this method compared to copying as bitmap:

  • Custom styles are lost
  • In/Out labels are lost
  • Notebook magnification value is lost

If there's a need, some of these can be remedied by explicitly transferring some notebook options from the SelectedNotebook to the newly created one.

Szabolcs
  • 24,728
  • 9
  • 85
  • 174
0

I think this should work without you needing to make a new notebook:

button = Button["Preview", 
   Module[{expr = NotebookRead@InputNotebook[]}, 
    If[expr =!= {}, 
     With[{img = 
        Rasterize[expr, 
         ImageFormattingWidth -> 
          First@(WindowSize /. 
             AbsoluteOptions[InputNotebook[], WindowSize])]}, 
      MessageDialog[
       Column[{"Would you like to perform the action?", 
         img}], {"Do it!" :> doIt[img], "Cancel" :> Null},
       WindowSize -> {First@ImageDimensions@img, All}]]]]];
CreateDialog[button,
 WindowFloating -> True,
 WindowClickSelect -> False,
 Selectable -> False
 ]

I used a little option searcher to find ImageFormattingWidth and by passing the image width as the window width you can make the dialog fit the picture nicely and still display the button.

Here's a demo of its results:

Rasterizing a cell from a pallete

b3m2a1
  • 158
  • 1
  • 7
0

Have you tried using ExportString[] to create the graphic into memory? (technically into a temp file, but what do you care:] )

ExportString[your_mathematica_stuff_here,"PNG",Background->None]

See the output on a colored background to verify transparent BG:

Framed[ImportString[ExportString[x^2,"PNG",Background->None]
                    ,"PNG"]
       ,Background->Yellow]

For images with many color variations (like 3D plots), I recommend JPEG2000 format, and for solid-colored images where transparency is not necessary, use GIF to preserve color detail.

Yes, you can control ImageSize when you export string of the image.


ExportString output and image type/size comparison

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Gregory Klopper
  • 2,285
  • 1
  • 14
  • 14
  • 1
    Have you tested this in a palette context? Please replace `Rasterize` in my code by `ImportString@ExportString[..., "PNG]`, put the button on a palette, as I described, and you'll see that this is plagued by the same problem as plain `Rasterize`. – Szabolcs Dec 05 '11 at 08:33