Skip to content

Position

src.helpers.position.align(axis: Union[Dimensions, list[Dimensions], None] = None, layer: Union[ArtLayer, LayerSet, None] = None, ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None) -> None

Align the currently active layer to current selection, vertically or horizontal.

Parameters:

Name Type Description Default
axis Dimensions | list[Dimensions] | None

Which axis to use when aligning the layer, can be provided as a single axis or list.

None
layer ArtLayer | LayerSet | None

ArtLayer or LayerSet to align. Uses active layer if not provided.

None
ref ArtLayer | LayerSet | ReferenceLayer | type[LayerDimensions] | None

Reference to align the layer within. Uses current selection if not provided.

None
Source code in src\helpers\position.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def align(
    axis: Union[Dimensions, list[Dimensions], None] = None,
    layer: Union[ArtLayer, LayerSet, None] = None,
    ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None
) -> None:
    """Align the currently active layer to current selection, vertically or horizontal.

    Args:
        axis: Which axis to use when aligning the layer, can be provided as a single axis or list.
        layer: ArtLayer or LayerSet to align. Uses active layer if not provided.
        ref: Reference to align the layer within. Uses current selection if not provided.
    """
    # Default axis is both
    axis = axis or [Dimensions.CenterX, Dimensions.CenterY]
    axis = [axis] if isinstance(axis, str) else axis
    x, y = 0, 0

    # Get the dimensions of layer and reference if not provided
    layer = layer or APP.activeDocument.activeLayer
    item: type[LayerDimensions] = get_layer_dimensions(layer)
    area: type[LayerDimensions] = ref if isinstance(ref, dict) else (
        get_dimensions_from_bounds(APP.activeDocument.selection.bounds)
        if not ref else get_layer_dimensions(ref))

    # Single axis provided
    for n in axis:
        if n in positions_horizontal:
            x = area[n] - item[n]
        if n in positions_vertical:
            y = area[n] - item[n]

    # Shift location using the position difference
    layer.translate(x, y)

src.helpers.position.align_all(layer: Union[ArtLayer, LayerSet, None] = None, ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None) -> None

Utility definition for passing CenterX and CenterY to align function.

Source code in src\helpers\position.py
79
80
81
82
83
84
def align_all(
    layer: Union[ArtLayer, LayerSet, None] = None,
    ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None
) -> None:
    """Utility definition for passing CenterX and CenterY to align function."""
    align([Dimensions.CenterX, Dimensions.CenterY], layer, ref)

src.helpers.position.align_vertical(layer: Union[ArtLayer, LayerSet, None] = None, ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None) -> None

Utility definition for passing CenterY to align function.

Source code in src\helpers\position.py
87
88
89
90
91
92
def align_vertical(
    layer: Union[ArtLayer, LayerSet, None] = None,
    ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None
) -> None:
    """Utility definition for passing CenterY to align function."""
    align(Dimensions.CenterY, layer, ref)

src.helpers.position.align_horizontal(layer: Union[ArtLayer, LayerSet, None] = None, ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None) -> None

Utility definition for passing CenterX to align function.

Source code in src\helpers\position.py
 95
 96
 97
 98
 99
100
def align_horizontal(
    layer: Union[ArtLayer, LayerSet, None] = None,
    ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None
) -> None:
    """Utility definition for passing CenterX to align function."""
    align(Dimensions.CenterX, layer, ref)

src.helpers.position.align_left(layer: Union[ArtLayer, LayerSet, None] = None, ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None) -> None

Utility definition for passing Left to align function.

Source code in src\helpers\position.py
103
104
105
106
107
108
def align_left(
    layer: Union[ArtLayer, LayerSet, None] = None,
    ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None
) -> None:
    """Utility definition for passing Left to align function."""
    align(Dimensions.Left, layer, ref)

src.helpers.position.align_right(layer: Union[ArtLayer, LayerSet, None] = None, ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None) -> None

Utility definition for passing Right to align function.

Source code in src\helpers\position.py
111
112
113
114
115
116
def align_right(
    layer: Union[ArtLayer, LayerSet, None] = None,
    ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None
) -> None:
    """Utility definition for passing Right to align function."""
    align(Dimensions.Right, layer, ref)

src.helpers.position.align_top(layer: Union[ArtLayer, LayerSet, None] = None, ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None) -> None

Utility definition for passing Top to align function.

Source code in src\helpers\position.py
119
120
121
122
123
124
def align_top(
    layer: Union[ArtLayer, LayerSet, None] = None,
    ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None
) -> None:
    """Utility definition for passing Top to align function."""
    align(Dimensions.Top, layer, ref)

src.helpers.position.align_bottom(layer: Union[ArtLayer, LayerSet, None] = None, ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None) -> None

Utility definition for passing Bottom to align function.

Source code in src\helpers\position.py
127
128
129
130
131
132
def align_bottom(
    layer: Union[ArtLayer, LayerSet, None] = None,
    ref: Union[ArtLayer, LayerSet, ReferenceLayer, type[LayerDimensions], None] = None
) -> None:
    """Utility definition for passing Bottom to align function."""
    align(Dimensions.Bottom, layer, ref)

src.helpers.position.position_between_layers(layer: Union[ArtLayer, LayerSet], top_layer: Union[ArtLayer, LayerSet], bottom_layer: Union[ArtLayer, LayerSet], docref: Optional[Document] = None) -> None

Align layer vertically between two reference layers.

Parameters:

Name Type Description Default
layer ArtLayer | LayerSet

Layer to align vertically

required
top_layer ArtLayer | LayerSet

Reference layer above the layer to be aligned.

required
bottom_layer ArtLayer | LayerSet

Reference layer below the layer to be aligned.

required
docref Document | None

Document reference, use active if not provided.

None
Source code in src\helpers\position.py
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
def position_between_layers(
    layer: Union[ArtLayer, LayerSet],
    top_layer: Union[ArtLayer, LayerSet],
    bottom_layer: Union[ArtLayer, LayerSet],
    docref: Optional[Document] = None
) -> None:
    """Align layer vertically between two reference layers.

    Args:
        layer: Layer to align vertically
        top_layer: Reference layer above the layer to be aligned.
        bottom_layer: Reference layer below the layer to be aligned.
        docref: Document reference, use active if not provided.
    """
    docref = docref or APP.activeDocument
    bounds = (0, top_layer.bounds[3], docref.width, bottom_layer.bounds[1])
    align_vertical(layer, get_dimensions_from_bounds(bounds))

src.helpers.position.position_dividers(dividers: list[Union[ArtLayer, LayerSet]], layers: list[Union[ArtLayer, LayerSet]], docref: Optional[Document] = None) -> None

Positions a list of dividers between a list of layers.

Parameters:

Name Type Description Default
dividers list[ArtLayer | LayerSet]

Divider layers to position, should contain 1 fewer objects than layers param.

required
layers list[ArtLayer | LayerSet]

Layers to position the dividers between.

required
docref Document | None

Document reference, use active if not provided.

None
Source code in src\helpers\position.py
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
def position_dividers(
    dividers: list[Union[ArtLayer, LayerSet]],
    layers: list[Union[ArtLayer, LayerSet]],
    docref: Optional[Document] = None
) -> None:
    """Positions a list of dividers between a list of layers.

    Args:
        dividers: Divider layers to position, should contain 1 fewer objects than layers param.
        layers: Layers to position the dividers between.
        docref: Document reference, use active if not provided.
    """
    for i in range(len(layers) - 1):
        position_between_layers(
            layer=dividers[i],
            top_layer=layers[i],
            bottom_layer=layers[i + 1],
            docref=docref)

src.helpers.position.spread_layers_over_reference(layers: list[ArtLayer], ref: ReferenceLayer, gap: Optional[Union[int, float]] = None, inside_gap: Union[int, float, None] = None, outside_matching: bool = True) -> None

Spread layers apart across a reference layer.

Parameters:

Name Type Description Default
layers list[ArtLayer]

List of ArtLayers or LayerSets.

required
ref ReferenceLayer

Reference used as the maximum height boundary for all layers given.

required
gap int | float | None

Gap between the top of the reference and the first layer, or between all layers if not provided.

None
inside_gap int | float | None

Gap between each layer, calculated using leftover space if not provided.

None
outside_matching bool

If enabled, will enforce top and bottom gap to match.

True
Source code in src\helpers\position.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
def spread_layers_over_reference(
    layers: list[ArtLayer],
    ref: ReferenceLayer,
    gap: Optional[Union[int, float]] = None,
    inside_gap: Union[int, float, None] = None,
    outside_matching: bool = True
) -> None:
    """Spread layers apart across a reference layer.

    Args:
        layers: List of ArtLayers or LayerSets.
        ref: Reference used as the maximum height boundary for all layers given.
        gap: Gap between the top of the reference and the first layer, or between all layers if not provided.
        inside_gap: Gap between each layer, calculated using leftover space if not provided.
        outside_matching: If enabled, will enforce top and bottom gap to match.
    """
    # Get reference dimensions if not provided
    height = ref.dims['height']

    # Calculate outside gap if not provided
    outside_gap = gap
    if not gap:
        total_space = height - sum(
            [get_layer_dimensions(layer)['height'] for layer in layers])
        outside_gap = total_space / (len(layers) + 1)

    # Position the top layer relative to the reference
    delta = (ref.bounds[1] + outside_gap) - layers[0].bounds[1]
    layers[0].translate(0, delta)

    # Calculate inside gap if not provided
    if gap and not inside_gap:
        # Calculate the inside gap
        ignored = 2 if outside_matching else 1
        spaces = len(layers) - 1 if outside_matching else len(layers)
        total_space = height - sum(
            [get_layer_dimensions(layer)['height'] for layer in layers])
        inside_gap = (total_space - (ignored * gap)) / spaces
    elif not gap:
        # Use the outside gap uniformly
        inside_gap = outside_gap

    # Position the bottom layers relative to the top
    space_layers_apart(layers, inside_gap)

src.helpers.position.space_layers_apart(layers: list[Union[ArtLayer, LayerSet]], gap: Union[int, float]) -> None

Position list of layers apart using a given gap.

Parameters:

Name Type Description Default
layers list[ArtLayer | LayerSet]

List of ArtLayers or LayerSets.

required
gap int | float

Gap in pixels.

required
Source code in src\helpers\position.py
225
226
227
228
229
230
231
232
233
234
235
def space_layers_apart(layers: list[Union[ArtLayer, LayerSet]], gap: Union[int, float]) -> None:
    """Position list of layers apart using a given gap.

    Args:
        layers: List of ArtLayers or LayerSets.
        gap: Gap in pixels.
    """
    # Position each layer relative to the one above it
    for i in range((len(layers) - 1)):
        delta = (layers[i].bounds[3] + gap) - layers[i + 1].bounds[1]
        layers[i + 1].translate(0, delta)

src.helpers.position.frame_layer(layer: Union[ArtLayer, LayerSet], ref: Union[ArtLayer, LayerSet, type[LayerDimensions]], smallest: bool = False, anchor: AnchorPosition = AnchorPosition.MiddleCenter, alignments: Union[Dimensions, list[Dimensions], None] = None, scale: int = 100) -> None

Scale and position a layer within the bounds of a reference.

Parameters:

Name Type Description Default
layer ArtLayer | LayerSet

Layer to scale and position.

required
ref ArtLayer | LayerSet | type[LayerDimensions]

Reference frame to position within.

required
smallest bool

Whether to scale to smallest or largest edge.

False
anchor AnchorPosition

Anchor position for scaling the layer.

MiddleCenter
alignments Dimensions | list[Dimensions] | None

Alignments used to position the layer.

None
scale int

Percentage of the reference size to scale to, defaults to 100.

100
Source code in src\helpers\position.py
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
def frame_layer(
    layer: Union[ArtLayer, LayerSet],
    ref: Union[ArtLayer, LayerSet, type[LayerDimensions]],
    smallest: bool = False,
    anchor: AnchorPosition = AnchorPosition.MiddleCenter,
    alignments: Union[Dimensions, list[Dimensions], None] = None,
    scale: int = 100,
) -> None:
    """Scale and position a layer within the bounds of a reference.

    Args:
        layer: Layer to scale and position.
        ref: Reference frame to position within.
        smallest: Whether to scale to smallest or largest edge.
        anchor: Anchor position for scaling the layer.
        alignments: Alignments used to position the layer.
        scale: Percentage of the reference size to scale to, defaults to 100.
    """
    # Get layer and reference dimensions
    layer_dim = get_layer_dimensions(layer)
    ref_dim = ref if isinstance(ref, dict) else get_layer_dimensions(ref)

    # Scale the layer to fit either the largest, or the smallest dimension
    action = min if smallest else max
    scale = scale * action(
        (ref_dim['width'] / layer_dim['width']),
        (ref_dim['height'] / layer_dim['height']))
    layer.resize(scale, scale, anchor)

    # Default alignments are center horizontal and vertical
    align(alignments or [Dimensions.CenterX, Dimensions.CenterY], layer, ref_dim)

src.helpers.position.frame_layer_by_height(layer: Union[ArtLayer, LayerSet], ref: Union[ArtLayer, LayerSet, type[LayerDimensions]], anchor: AnchorPosition = AnchorPosition.MiddleCenter, alignments: Union[Dimensions, list[Dimensions], None] = None, scale: int = 100) -> None

Scale and position a layer based on the height of a reference layer.

Parameters:

Name Type Description Default
layer ArtLayer | LayerSet

Layer to scale and position.

required
ref ArtLayer | LayerSet | type[LayerDimensions]

Reference frame to position within.

required
anchor AnchorPosition

Anchor position for scaling the layer.

MiddleCenter
alignments Dimensions | list[Dimensions] | None

Alignments used to position the layer.

None
scale int

Percentage of the reference size to scale to, defaults to 100.

100
Source code in src\helpers\position.py
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
def frame_layer_by_height(
    layer: Union[ArtLayer, LayerSet],
    ref: Union[ArtLayer, LayerSet, type[LayerDimensions]],
    anchor: AnchorPosition = AnchorPosition.MiddleCenter,
    alignments: Union[Dimensions, list[Dimensions], None] = None,
    scale: int = 100,
) -> None:
    """Scale and position a layer based on the height of a reference layer.

    Args:
        layer: Layer to scale and position.
        ref: Reference frame to position within.
        anchor: Anchor position for scaling the layer.
        alignments: Alignments used to position the layer.
        scale: Percentage of the reference size to scale to, defaults to 100.
    """
    # Get reference dimensions
    ref_dim = ref if isinstance(ref, dict) else get_layer_dimensions(ref)

    # Scale the layer to fit the height of the reference
    scale = scale * (ref_dim['height'] / get_layer_height(layer))
    layer.resize(scale, scale, anchor)

    # Default alignments are center horizontal and vertical
    align(alignments or [Dimensions.CenterX, Dimensions.CenterY], layer, ref_dim)

src.helpers.position.frame_layer_by_width(layer: Union[ArtLayer, LayerSet], ref: Union[ArtLayer, LayerSet, type[LayerDimensions]], anchor: AnchorPosition = AnchorPosition.MiddleCenter, alignments: Union[Dimensions, list[Dimensions], None] = None, scale: int = 100) -> None

Scale and position a layer based on the width of a reference layer.

Parameters:

Name Type Description Default
layer ArtLayer | LayerSet

Layer to scale and position.

required
ref ArtLayer | LayerSet | type[LayerDimensions]

Reference frame to position within.

required
anchor AnchorPosition

Anchor position for scaling the layer.

MiddleCenter
alignments Dimensions | list[Dimensions] | None

Alignments used to position the layer.

None
scale int

Percentage of the reference size to scale to, defaults to 100.

100
Source code in src\helpers\position.py
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
def frame_layer_by_width(
    layer: Union[ArtLayer, LayerSet],
    ref: Union[ArtLayer, LayerSet, type[LayerDimensions]],
    anchor: AnchorPosition = AnchorPosition.MiddleCenter,
    alignments: Union[Dimensions, list[Dimensions], None] = None,
    scale: int = 100,
) -> None:
    """Scale and position a layer based on the width of a reference layer.

    Args:
        layer: Layer to scale and position.
        ref: Reference frame to position within.
        anchor: Anchor position for scaling the layer.
        alignments: Alignments used to position the layer.
        scale: Percentage of the reference size to scale to, defaults to 100.
    """
    # Get reference dimensions
    ref_dim = ref if isinstance(ref, dict) else get_layer_dimensions(ref)

    # Scale the layer to fit the height of the reference
    scale = scale * (ref_dim['width'] / get_layer_width(layer))
    layer.resize(scale, scale, anchor)

    # Default alignments are center horizontal and vertical
    align(alignments or [Dimensions.CenterX, Dimensions.CenterY], layer, ref_dim)

src.helpers.position.check_reference_overlap(layer: Optional[ArtLayer], ref_bounds: tuple[int, int, int, int], docsel: Optional[Selection] = None)

Checks if a layer is overlapping with given set of bounds.

Parameters:

Name Type Description Default
layer ArtLayer | None

Layer to check collision for.

required
ref_bounds tuple[int, int, int, int]

Bounds to check collision with.

required
docsel Selection | None

Selection object, pull from document if not provided.

None

Returns:

Type Description

Bounds if overlap exists, otherwise None.

Source code in src\helpers\position.py
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
def check_reference_overlap(
    layer: Optional[ArtLayer],
    ref_bounds: tuple[int, int, int, int],
    docsel: Optional[Selection] = None
):
    """Checks if a layer is overlapping with given set of bounds.

    Args:
        layer: Layer to check collision for.
        ref_bounds: Bounds to check collision with.
        docsel: Selection object, pull from document if not provided.

    Returns:
        Bounds if overlap exists, otherwise None.
    """
    select_bounds(ref_bounds, selection=docsel)
    select_overlapping(layer)
    if bounds := check_selection_bounds(docsel):
        docsel.deselect()
        return ref_bounds[1] - bounds[3]
    return 0

src.helpers.position.clear_reference_vertical(layer: ArtLayer, ref: ReferenceLayer, docsel: Optional[Selection] = None) -> Union[int, float]

Nudges a layer clear vertically of a given reference layer or area.

Parameters:

Name Type Description Default
layer ArtLayer

Layer to nudge, so it avoids the reference area.

required
ref ReferenceLayer

Layer or bounds area to nudge clear of.

required
docsel Selection | None

Selection object, pull from document if not provided.

None

Returns:

Type Description
int | float

The number of pixels layer was translated by (negative or positive indicating direction).

Source code in src\helpers\position.py
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
def clear_reference_vertical(
    layer: ArtLayer,
    ref: ReferenceLayer,
    docsel: Optional[Selection] = None
) -> Union[int, float]:
    """Nudges a layer clear vertically of a given reference layer or area.

    Args:
        layer: Layer to nudge, so it avoids the reference area.
        ref: Layer or bounds area to nudge clear of.
        docsel: Selection object, pull from document if not provided.

    Returns:
        The number of pixels layer was translated by (negative or positive indicating direction).
    """
    # Use active layer if not provided
    docsel = docsel or APP.activeDocument.selection
    delta = check_reference_overlap(layer=layer, ref_bounds=ref.bounds, docsel=docsel)

    # Check if selection is empty, if not translate our layer to clear the reference
    if delta < 0:
        layer.translate(0, delta)
        return delta
    return 0

src.helpers.position.clear_reference_vertical_multi(text_layers: list[ArtLayer], ref: ReferenceLayer, loyalty_ref: ReferenceLayer, space: Union[int, float], uniform_gap: bool = False, font_size: Optional[float] = None, step: float = 0.2, docref: Optional[Document] = None, docsel: Optional[Selection] = None) -> None

Shift or resize multiple text layers to prevent vertical collision with a reference area.

Note

Used on Planeswalker cards to allow multiple text abilities to clear the loyalty box.

Parameters:

Name Type Description Default
text_layers list[ArtLayer]

Ability text layers to nudge or resize.

required
ref ReferenceLayer

Reference area ability text layers must fit inside.

required
loyalty_ref ReferenceLayer

Reference area that covers the loyalty box.

required
space int | float

Minimum space between planeswalker abilities.

required
uniform_gap bool

Whether the gap between abilities should be the same between each ability.

False
font_size float | None

The current font size of the text layers, if known. Otherwise, calculate automatically.

None
step float

The amount of font size and leading to step down each iteration.

0.2
docref Document | None

Reference document, use active if not provided (improves performance).

None
docsel Selection | None

Selection object, pull from document if not provided (improves performance).

None
Source code in src\helpers\position.py
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
def clear_reference_vertical_multi(
    text_layers: list[ArtLayer],
    ref: ReferenceLayer,
    loyalty_ref: ReferenceLayer,
    space: Union[int, float],
    uniform_gap: bool = False,
    font_size: Optional[float] = None,
    step: float = 0.2,
    docref: Optional[Document] = None,
    docsel: Optional[Selection] = None
) -> None:
    """Shift or resize multiple text layers to prevent vertical collision with a reference area.

    Note:
        Used on Planeswalker cards to allow multiple text abilities to clear the loyalty box.

    Args:
        text_layers: Ability text layers to nudge or resize.
        ref: Reference area ability text layers must fit inside.
        loyalty_ref: Reference area that covers the loyalty box.
        space: Minimum space between planeswalker abilities.
        uniform_gap: Whether the gap between abilities should be the same between each ability.
        font_size: The current font size of the text layers, if known. Otherwise, calculate automatically.
        step: The amount of font size and leading to step down each iteration.
        docref: Reference document, use active if not provided (improves performance).
        docsel: Selection object, pull from document if not provided (improves performance).
    """
    # Return if adjustments weren't provided
    if not loyalty_ref:
        return

    # Establish fresh data
    if font_size is None:
        font_size = get_font_size(text_layers[0])
    layers = text_layers.copy()
    movable = len(layers)-1

    # Calculate inside gap
    total_space = ref.dims['height'] - sum([get_layer_height(layer) for layer in text_layers])
    if not uniform_gap:
        inside_gap = ((total_space - space) - (ref.bounds[3] - layers[-1].bounds[1])) / movable
    else:
        inside_gap = total_space / (len(layers) + 1)
    leftover = (inside_gap - space) * movable

    # Does the bottom layer overlap with the loyalty box?
    delta = check_reference_overlap(
        layer=layers[-1],
        ref_bounds=loyalty_ref.bounds,
        docsel=docsel)
    if delta >= 0:
        return

    # Calculate the total distance needing to be covered
    total_move = 0
    layers.pop(0)
    for n, lyr in enumerate(layers):
        total_move += math.fabs(delta) * ((len(layers) - n)/len(layers))

    # Text layers can just be shifted upwards
    if total_move < leftover:
        layers.reverse()
        for n, lyr in enumerate(layers):
            move_y = delta * ((len(layers) - n)/len(layers))
            lyr.translate(0, move_y)
        return

    # Layer gap would be too small, need to resize text then shift upward
    font_size -= step
    for lyr in text_layers:
        set_text_size_and_leading(
            layer=lyr,
            size=font_size,
            leading=font_size)

    # Space apart planeswalker text evenly
    spread_layers_over_reference(
        layers=text_layers,
        ref=ref,
        gap=space if not uniform_gap else None,
        outside_matching=False)

    # Check for another iteration
    clear_reference_vertical_multi(
        text_layers=text_layers,
        ref=ref,
        loyalty_ref=loyalty_ref,
        space=space,
        uniform_gap=uniform_gap,
        font_size=font_size,
        docref=docref,
        docsel=docsel)