Skip to content
Open
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ How the project is developed and shipped is documented separately:
- Undo and redo for strokes, erasing, containers, text edits, and transformations
- Versioned ZIP-based `.wboard` documents with an embedded `preview.png`. Explorer shows that picture as the file thumbnail in the released install. The VS Code extension in `vscode/sqlbi-whiteboard` opens the same picture instead of the ZIP.
- Markdown `.wimport` recipes that build image and text containers from headings
- **File → Export** to PowerPoint or PDF: the board is cut into areas where it is empty, one slide or page per area, with an overview first. A deck carries the text containers in the speaker notes; a PDF has a bookmark per page and can put the whole board on one page to zoom into. The dialog shows the areas numbered on the board and updates as the settings change. [docs/export.md](docs/export.md) explains how areas are chosen
- **File → Export** to PowerPoint or PDF: the board is cut into areas where it is empty, one slide or page per area, with an overview first. A deck carries the text containers in the speaker notes, or, as Editable slides, puts images and text containers on the slide as objects with the ink over them; a PDF has a bookmark per page and can put the whole board on one page to zoom into. The dialog shows the areas numbered on the board and updates as the settings change. [docs/export.md](docs/export.md) explains how areas are chosen
- An intentionally small floating toolbar
- A File / Edit / View / Help tab strip. Click a tab for a one-row command strip over the canvas
- Preferences for the startup monitor, full-screen start, finger drawing, mouse drawing, the pen button, snippet format order, laser trail timing and weight, toolbar position and layout, keeping the Eraser on the toolbar for a pen that has no reverse end, and (except Store installs) a daily new-version check
Expand Down
8 changes: 6 additions & 2 deletions docs/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,8 +579,12 @@ scaled down and the dialog says by how much, rather than being tiled across slid

The slide is a picture rendered by the same `BoardSurface` that draws the screen, at twice
full HD, so calligraphy, the highlighter, and SVG come out as they are seen. The text
containers go in the speaker notes so DAX and SQL can still be copied. An editable deck,
with native text boxes and images, is a later phase.
containers go in the speaker notes so DAX and SQL can still be copied. **Editable** is the
other slide content: images as pictures, text containers as text boxes carrying the
syntax colors the screen shows, and all the ink as one transparent picture on top, placed
through the same camera the picture would have used. It is best effort where the picture
is exact, which is why the picture stays the default. Ink as freeform shapes was left
out: it would be a second stroke renderer to keep in step with the first.

`DocumentFormat.OpenXml` writes the deck and `PdfSharp` the PDF: Microsoft's own SDK and
a long-lived MIT library, both managed-only, so they pass the tests decision 21 set for a
Expand Down
25 changes: 25 additions & 0 deletions docs/export-decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,28 @@ which phase made it and why.
- **The overview page** works as for the deck: first, only with two or more areas.
- **`PdfSharp` 6.2.4** is the writer, MIT and managed-only. Fonts come from the Windows
fonts folder through its platform resolver; the export runs only on Windows.

## E3 — Editable deck

- **Slide content is a choice on the dialog, Picture or Editable**, shown for PowerPoint
only. Picture stays the default: it is exact, and Editable is best effort by design.
- **Elements are placed in the picture's own pixel space.** The rasterizer exposes the
camera it would have used, and every image, text box, and the ink overlay is mapped
through it, so switching between the two modes moves nothing.
- **Bitmaps go in as they are when they are PNG or JPEG**, sniffed from the bytes rather
than trusted to the stored content type, because boards written before SVG support have
none. SVG, BMP, and GIF are rasterized at the size they take on the slide, through the
same code the clipboard uses.
- **A text container is one rounded text box**: the language service's title as the first
paragraph in Segoe UI semibold, then the body in the language's monospace font, with the
syntax colors and weights carried over as runs. The text box has PowerPoint autofit
switched off, so the text keeps the size it had on the board; a container that has
been resized smaller than its text on the board is clipped there, and overflows here.
- **The ink is one transparent PNG over everything**, rendered by the same surface with
the background off and only strokes drawn. It sits above every container, which is
wrong only for a stroke drawn before an image was dropped on top of it.
- **A LiveView goes in as its current frame**, or its saved snapshot when nothing is
connected. A missing image (an asset the board no longer has) is left out rather than
exported as a placeholder.
- **The picture is still rendered for an editable page** and kept as the page's own
picture in the model, so a page can fall back to it; PowerPoint does not receive it.
1 change: 1 addition & 0 deletions site/guide.html
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ <h3 id="liveview">LiveView</h3>
<h3>Files</h3>
<p>The <span class="ui">File</span> row holds five commands: <span class="ui">New</span>, <span class="ui">Open</span>, <span class="ui">Save</span>, <span class="ui">Save As</span>, and <span class="ui">Export</span>. <kbd>Ctrl</kbd><span class="plus">+</span><kbd>S</kbd> and <kbd>Ctrl</kbd><span class="plus">+</span><kbd>O</kbd> save and open a <code>.wboard</code> without opening the row at all.</p>
<p><span class="ui">Export</span> (<kbd>Ctrl</kbd><span class="plus">+</span><kbd>E</kbd>) writes the board as a PowerPoint deck. The board is cut into areas wherever it is empty: a picture or a note keeps the ink drawn on it, and a stroke that spans two of them keeps them together. Each area becomes a slide, with the text containers in the speaker notes so that DAX and SQL can be copied, and an overview slide first shows where every slide sits on the board. The dialog previews the areas, numbered, and the <span class="ui">Gap between areas</span> slider and <span class="ui">Smallest text on a slide</span> change the cut as you move them. <span class="ui">Drawing order</span> puts slides in the order the areas were begun; <span class="ui">Reading order</span> goes top-left to bottom-right.</p>
<p><span class="ui">Slide content</span> chooses between <span class="ui">Picture</span>, one exact picture of the area, and <span class="ui">Editable</span>, which puts images and text containers on the slide as PowerPoint objects, keeps the DAX and SQL colors as text runs, and lays all the ink over them as one transparent picture. Editable is for a deck that will be reworked; Picture is for one that will be shown.</p>
<p>Choose <span class="ui">PDF</span> in the same dialog for a document instead: the same areas, one per A4 or Letter page in landscape, each with a bookmark, and a footer with the board name, the date, and the page number. <span class="ui">Whole board on one page</span> writes a single page the shape of the board, to be read by zooming, which suits a board that is one drawing.</p>
<p>The released installer registers that type and <code>.wimport</code>. The pre-release (Dev) channel does not, so it can sit beside a released copy without stealing the file association.</p>

Expand Down
21 changes: 21 additions & 0 deletions src/SQLBI.Whiteboard.Core/Settings/ExportSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@ public enum ExportSlideAspect
Standard = 1,
}

public enum ExportSlideContent
{
/// <summary>
/// One picture of the area, exactly as the screen draws it.
/// </summary>
Picture = 0,

/// <summary>
/// Images and text containers as PowerPoint objects, with the ink as one
/// transparent picture over them.
/// </summary>
Editable = 1,
}

/// <summary>
/// What the export dialog remembers between uses. These are not in Preferences:
/// they belong to the dialog that shows their effect.
Expand All @@ -57,6 +71,8 @@ public sealed class ExportSettings

public ExportSlideAspect SlideAspect { get; set; } = ExportSlideAspect.Wide;

public ExportSlideContent SlideContent { get; set; } = ExportSlideContent.Picture;

public ExportPageSize PageSize { get; set; } = ExportPageSize.A4;

/// <summary>
Expand Down Expand Up @@ -97,6 +113,11 @@ public static ExportSettings Normalize(ExportSettings? settings)
result.PageSize = ExportPageSize.A4;
}

if (!Enum.IsDefined(result.SlideContent))
{
result.SlideContent = ExportSlideContent.Picture;
}

result.GapThreshold = double.IsFinite(result.GapThreshold)
? Math.Clamp(
result.GapThreshold,
Expand Down
43 changes: 42 additions & 1 deletion src/SQLBI.Whiteboard.Export/ExportPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,49 @@ namespace SQLBI.Whiteboard.Export;
/// <summary>
/// One slide or page: a title, optional notes, and the picture that fills it. The pixel
/// size is what the layout scales by, so the PNG itself is never decoded on the way out.
/// When <paramref name="Elements"/> is given, a slide is built from them instead of the
/// picture, in the same pixel coordinate space, and the picture is kept for a page that
/// cannot take elements.
/// </summary>
public sealed record ExportPage(string Title, string? Notes, byte[] Png, int PixelWidth, int PixelHeight);
public sealed record ExportPage(
string Title,
string? Notes,
byte[] Png,
int PixelWidth,
int PixelHeight,
IReadOnlyList<SlideElement>? Elements = null);

/// <summary>
/// A rectangle in the page's pixel space: the same space the picture is measured in.
/// </summary>
public readonly record struct SlideRect(double X, double Y, double Width, double Height);

public abstract record SlideElement(SlideRect Bounds);

/// <summary>
/// A picture placed as a picture: an image container, a LiveView frame, or the ink
/// overlay that covers the whole page. Content type is "image/png" or "image/jpeg".
/// </summary>
public sealed record SlideImageElement(SlideRect Bounds, byte[] Data, string ContentType) : SlideElement(Bounds);

public sealed record SlideTextRun(string Text, uint Argb, bool Bold, bool Italic);

/// <summary>
/// A text container as a text box: the title on its own first line, then the body as
/// runs whose colors and weights are the ones the screen shows. A run may contain line
/// breaks; each starts a new paragraph. Sizes and the padding are in page pixels.
/// </summary>
public sealed record SlideTextElement(
SlideRect Bounds,
string Title,
IReadOnlyList<SlideTextRun> Runs,
string FontFamily,
double TitleFontSize,
double BodyFontSize,
double Padding,
uint BackgroundArgb,
uint BorderArgb,
uint TextArgb) : SlideElement(Bounds);

/// <summary>16:9 or 4:3.</summary>
public enum SlideAspect
Expand Down
Loading