I'm trying to tightly wrap some expressions in containers (could be Panel, Pane, maybe Framed in some cases -- it varies on the use-case) the way a normal expression cell's frame are laid-out.
That is, if you select a simple expression like x, you get the following:
Whereas, even with PaneMargins and ImageMargins set to 0, selecting e.g. a Pane results in this:
One way to calculate the borders/margins between the inner expression and the bounding box of the expression when placed inside a container such as Pane is using ImageBorders. That part is straightforward enough, and not the subject of this question (although I'd welcome more efficient methods than rasterizing and then using ImageBorders! EDIT: Found an alternative, see the end of this post.).
The subject of this question is: Can someone provide a general-case description, or link to documentation, for the alignment behavior of Pane, Panel, Framed, and any other bonus containers, with respect to options such as Alignment and FrameMargins? Particularly, when ImageSize is less than what would be returned by ImageSize@Rasterize@expr and FrameMargins contains negative values, I find that the resulting output between the different containers varies quite a bit.
EDIT: As suggested by Carl Woll in a comment below, setting ContentPadding->False ought to solve this question. However, I used that in my example below, and it didn't work. I believe that wrapping e.g. a Pane in e.g. a Grid causes the Pane's ContentPadding option to not be respected. Maybe that behavior is the crux of this post.
It's easier to post a GIF animation exported from Mathematica than to explain much further. On first inspection, it looks like there are two classes of containers: Pane and Row on the one hand, and Framed and Panel on the other. But if you look closely, as glyphs "scroll into the inside of each container", the behavior is different between e.g. Framed and Panel when the margins are negative. It makes getting a general-case solution tough when you're trying to tightly wrap expressions, but don't want them clipped.
Animation below:
And here is a version with the other margin dimension being animated:
REFERENCE:
The code used to generate the above plots is shown below:
MakeContainerSwatch[expr_, height_, alignment_] :=
Module[{rulesCommon, rulesPadding, rulesFrame, rulesColor,
rulesColorPane, align, margins, pane1, pane2, pane3, framed1,
framed2, framed3, panel1, panel2, panel3, row1, row2, row3},
rulesCommon = Sequence[
ImageSize -> {Automatic, height},
ImageMargins -> {{0, 0}, {0, 0}},
BaseStyle -> "Output"
];
rulesPadding = Sequence[ContentPadding -> False];
rulesFrame = Sequence[Frame -> True];
rulesColor = Sequence[Background -> LightBlue];
rulesColorPane =
Sequence[DefaultBaseStyle -> {Background -> LightBlue}];
align[y_] = Alignment -> {Center, y};
margins[align_] = FrameMargins -> {{0, 0}, align};
pane1 =
Pane[expr, rulesCommon, rulesPadding, rulesColorPane, align[-1],
margins[alignment]];
pane2 =
Pane[expr, rulesCommon, rulesPadding, rulesColorPane, align[0],
margins[alignment]];
pane3 =
Pane[expr, rulesCommon, rulesPadding, rulesColorPane, align[1],
margins[alignment]];
framed1 =
Framed[expr, rulesCommon, rulesPadding, rulesColor, align[-1],
margins[alignment]];
framed2 =
Framed[expr, rulesCommon, rulesPadding, rulesColor, align[0],
margins[alignment]];
framed3 =
Framed[expr, rulesCommon, rulesPadding, rulesColor, align[1],
margins[alignment]];
panel1 =
Panel[expr, rulesCommon, rulesPadding, rulesColor, align[-1],
margins[alignment]];
panel2 =
Panel[expr, rulesCommon, rulesPadding, rulesColor, align[0],
margins[alignment]];
panel3 =
Panel[expr, rulesCommon, rulesPadding, rulesColor, align[1],
margins[alignment]];
row1 = Row[{expr}, rulesCommon, rulesFrame, rulesColor, align[-1],
margins[alignment]];
row2 = Row[{expr}, rulesCommon, rulesFrame, rulesColor, align[0],
margins[alignment]];
row3 = Row[{expr}, rulesCommon, rulesFrame, rulesColor, align[1],
margins[alignment]];
{"pn", pane1, pane2, pane3, "fr", framed1, framed2, framed3, "pl",
panel1, panel2, panel3, "rw", row1, row2, row3}
]
sampleExpression = "a\nb\nc\nd\ne\nf\ng\nh";
dummyPane1 = Pane["*", BaseStyle -> "Output"];
dummyPane2 = Pane["\n\n\n\n\n\n\n\n", BaseStyle -> "Output"];
containerSwatch := With[
{
bottomLabel = StringTemplate["Bottom: bot"][<|"bot" -> bot|>],
topLabel = StringTemplate["Top: top"][<|"top" -> top|>]
},
Grid[List /@ {
Row@{
Panel[
ParametricPlot[{bot, y}, {y, 0, 10},
PlotRange -> {{-40, 40}, {0, 10}}, PlotLabel -> bottomLabel,
Axes -> {True, False}, ImageSize -> Medium],
ImageSize -> Scaled[0.49], Alignment -> Center],
Panel[
ParametricPlot[{top, y}, {y, 0, 10},
PlotRange -> {{-40, 40}, {0, 10}}, PlotLabel -> topLabel,
Axes -> {True, False}, ImageSize -> Medium],
ImageSize -> Scaled[0.49], Alignment -> Center]
},
Grid@List@{
StyleHeading["Clipped: "],
Splice@
Prepend[dummyPane2]@
MakeContainerSwatch[sampleExpression,
154 - 80, {bot, top}],
StyleHeading[", Oversized: "],
Splice@
Prepend[dummyPane2]@
MakeContainerSwatch[sampleExpression, 154 + 80, {bot, top}]
}
},
ItemSize -> Full
]
]
containerSwatchAnimation1 =
Table[containerSwatch, {top, -40, 40, 20}, {bot, -40, 40, 2}];
containerSwatchAnimation2 =
Table[containerSwatch, {bot, -40, 40, 20}, {top, -40, 40, 2}];
Export["containerSwatchAnimation1.gif",
Flatten@containerSwatchAnimation1, ImageResolution -> 72/2]
Export["containerSwatchAnimation2.gif",
Flatten@containerSwatchAnimation2, ImageResolution -> 72/2]
EDIT: For posterity, here is a fast way to get the size you'd need to use to make a tightly-wrapped container. Credit goes to this post by b3m2a1: https://mathematica.stackexchange.com/a/166772/76328
FrontEndExecute@
GetBoundingBoxSizePacket@
Cell[
BoxData["aaaaaaa\na\na\na\na" // ToBoxes],
"Output",
PageWidth -> Infinity,
ShowCellBracket -> False,
CellMargins -> {{0, 0}, {0, 0}}
]
It's not the same as using ImageRaster and then the combination of ImageDimensions and BorderDimensions, but it's so much faster, and if I get the ContentPadding option (noted by Carl Woll below) working with Grids, it'll be sufficient for my task-at-hand (a ragged Grid of Rows of Panes of alternating colors, where the Rows are expected to line-wrap, but you want the Panes to be the same height for aesthetics).










Pane[x,ContentPadding->False]– Carl Woll Jan 12 '21 at 15:10PanewithContentPadding->Falseis what I'm looking for, except for one catch: I usedContentPadding->Falsein the above example, and it was ignored. I wouldn't have dug further if you hadn't posted, so thanks. It looks like theContentPaddingoption does what I want, EXCEPT if you then nest thePanein e.g. aGrid, in which case it gets ignored. Any ideas on how to work around that? Nesting the containers is a common use-case for me. I may switch from usingGridto manually laying-out everything withRows. – Sean Jan 13 '21 at 20:03ContentPaddingat the time I tried it (it turns out that mySpacingsspecification was poor, and I did not specifyItemSizes->{0,0}:Grid[List@List@Framed[".", ImageMargins -> {{0, 0}, {0, 0}}, FrameMargins -> {{0, 0},{0, 0}}, ContentPadding -> False], Frame -> All, Spacings -> 0]– Sean Jan 14 '21 at 16:17ContentPaddingalong the way. I was mostly usingPanes andPanels during testing (and using background color to determine where the margins played-in). I think I had gotten a "quasi-solution" working for them involving negative margins, but when I put them into an outerGrid, I noticed that there was now padding inside the colored areas in thePanes andPanels. I'd have to go back and check my notes to remember exactly how I created the situation, but I think I have a solution now, so it's less pressing. ;-) – Sean Jan 14 '21 at 18:19ExportPacketwill be much quicker than usinggraphicsInformationon multipleGraphicsobjects", do you know of a way to useExportPacketin this batch manner, but still get back granular size info for a collection of expressions? I tried aFrontEndExecute@Thread@ExportPacket@...method, which worked, but gave no performance boost at all... – Sean Jan 15 '21 at 01:00