Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Fixed

- **A figure whose art is the picture's own placeholder is one picture.** Art marked as the picture
inline (a Markdown image laid out as a figure) carried both marks, so MXP, Pueblo and BBCode wrote the
picture twice or dropped its cells, and the text beside it moved. A figure's rows now carry its mark
alone.
- **A figure's picture reaches MXP, Pueblo and BBCode.** A `Figure` laid out as text marked its picture
only for a terminal that draws pictures in its cells, so every other format wrote the art alone and an
MXP client was never sent `<IMAGE>`. Its rows (or its `[description]`) now always carry the picture
Expand Down
28 changes: 28 additions & 0 deletions MarkupString.Tests/Layout/FigurePictureTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,34 @@ public async Task ArtOfLineBreaksAloneIsNoArt()
await Assert.That(laid.ToPlainText().TrimEnd()).IsEqualTo("[A cat]");
}

/// <summary>
/// Art that is the picture's own placeholder, marked as the picture inline, as a Markdown image laid out as
/// a figure is: still one picture, and the words beside it start where they do in the plain text.
/// </summary>
[Test]
[Arguments("mxp")]
[Arguments("pueblo")]
[Arguments("bbcode")]
public async Task ArtMarkedAsThePictureIsStillOnePicture(string format)
{
var placeholder = MarkupText.Wrap(Cat, "[image: A cat]");
var figure = new Figure(Cat, placeholder) { Float = FigureFloat.Left, Beside = new TextBlock(MarkupText.Plain("The cat sits by the fire.")) };
var laid = BlockLayout.Build(figure, 40);
var markupFormat = format switch { "mxp" => MarkupFormat.Mxp, "pueblo" => MarkupFormat.Pueblo, _ => MarkupFormat.BBCode };
var tag = format switch
{
"mxp" => "<IMAGE cat.png URL=https://example.test/img/>",
"pueblo" => "<img src=\"https://example.test/img/cat.png\" alt=\"A cat\">",
_ => "[img]https://example.test/img/cat.png[/img]",
};

var first = Lines(laid, markupFormat)[0];

await Assert.That(Count(first, tag)).IsEqualTo(1);
await Assert.That(first.Replace(tag, string.Empty).IndexOf("The cat", StringComparison.Ordinal))
.IsEqualTo(laid.ToPlainText().IndexOf("The cat", StringComparison.Ordinal));
}

/// <summary>The words beside a floated picture stay in the column they are in for a reader of the art.</summary>
[Test]
public async Task TextBesideAFloatedPictureStaysWhereItWas()
Expand Down
21 changes: 20 additions & 1 deletion MarkupString/Layout/Blocks/Figure.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Immutable;
namespace MarkupString.Layout;

/// <summary>
Expand Down Expand Up @@ -118,10 +119,28 @@ private MarkupText[] Marked(MarkupText[] rows, int columns)
{
var marked = new MarkupText[rows.Length];
for (var row = 0; row < rows.Length; row++)
marked[row] = MarkupText.Wrap(Image with { Row = new PictureRow(row, rows.Length, columns) }, rows[row]);
marked[row] = MarkupText.Wrap(Image with { Row = new PictureRow(row, rows.Length, columns) }, Unpictured(rows[row]));
return marked;
}

/// <summary>
/// <paramref name="text"/> without the pictures it carries. Art is often the picture's own placeholder,
/// already marked as the picture inline; under the figure's mark that would be a second picture in the
/// same cells, written twice by a format with an element for it and pushing aside what is beside it.
/// </summary>
private static MarkupText Unpictured(MarkupText text)
{
if (!text.Runs.Any(run => run.Markups.Any(markup => markup is ImageMarkup))) return text;

var runs = ImmutableArray.CreateBuilder<Run>(text.Runs.Length);
foreach (var run in text.Runs)
{
var kept = run.Markups.Where(markup => markup is not ImageMarkup).ToArray();
if (kept.Length > 0) runs.Add(run with { Markups = MarkupSet.Of(kept) });
}
return new MarkupText(text.Text, runs.ToImmutable());
}

/// <inheritdoc/>
public override void DrawLinear(LayoutContext context, int width, IList<MarkupText> lines)
{
Expand Down
Loading