namespace AIStudio.Tools.PowerPoint; /// /// One slide as the model describes it: a layout family of the template, and the content of its slots. /// /// /// The lists fill the slots of their role in reading order: the first text goes into "text 1" of the /// catalog, the second into "text 2", and so on. An empty entry keeps its slot empty, and the /// entries after it keep their slots. A slot left without content is removed from the slide, so /// that no prompt text of the template stays behind. Only a picture slot may stay, when the user /// asks for it, see PresentationWriteOptions.KeepEmptyPicturePlaceholders. /// /// A slide may refer to a slide of the template instead of a layout. Without content, it keeps that /// slide as it is. With content, the content replaces what the slots of that slide hold, and every /// slot it leaves empty keeps the content of the template, unless the slide removes it. What the /// template slide carries besides its slots stays as well, unless the slide removes it. Where the /// template slide carries copies of a slot, e.g., a second heading with its text, the numbers go on /// through them: "label 2" is the first copy of a layout with a single label. /// /// The ID of the layout family, e.g., "L3"; not used when the slide refers to a template slide, which keeps its own layout. /// Whether a new chapter starts with this slide, which opens a section of the presentation. /// The title; empty when the slide has none. /// The subtitle; empty when the slide has none. /// The content of the text slots. /// The content of the label slots. /// The pictures for the picture slots, as 1-based numbers of the attached pictures. /// The content of the table slot, if any. /// The number of the template slide the slide keeps or edits; null for a new slide. internal sealed record SlideSpec(string Layout, bool StartsChapter, string Title, string Subtitle, IReadOnlyList Texts, IReadOnlyList Labels, IReadOnlyList Pictures, SlideTableSpec? Table, int? TemplateSlide = null) { /// /// What the slide removes from the template slide it edits, by the names the model read: slots, /// e.g., "label 1", and elements, e.g., "element 2". Empty for every other slide. /// public IReadOnlyList Removals { get; init; } = []; /// /// Whether the slide brings content of its own, rather than keeping a template slide as it is. /// /// /// Removing something changes a template slide as well. /// public bool HasContent => this.Title.Length > 0 || this.Subtitle.Length > 0 || this.Texts.Any(text => text.Paragraphs.Count > 0) || this.Labels.Any(label => label.Length > 0) || this.Pictures.Count > 0 || this.Table is not null || this.Removals.Count > 0; /// /// Whether the slide has content for a slot. /// /// The role of the slot. /// The number of the slot among the slots of its role, starting at 1. public bool Fills(PlaceholderRole role, int ordinal) => role switch { PlaceholderRole.TITLE => this.Title.Length > 0, PlaceholderRole.SUBTITLE => this.Subtitle.Length > 0, PlaceholderRole.TEXT => ordinal >= 1 && ordinal <= this.Texts.Count && this.Texts[ordinal - 1].Paragraphs.Count > 0, PlaceholderRole.LABEL => ordinal >= 1 && ordinal <= this.Labels.Count && this.Labels[ordinal - 1].Length > 0, PlaceholderRole.PICTURE => ordinal >= 1 && ordinal <= this.Pictures.Count, PlaceholderRole.TABLE => ordinal == 1 && this.Table is { Headers.Count: > 0 }, _ => false, }; /// /// Whether the slide removes the content a slot of its template slide holds. /// /// The slot. public bool Removes(TemplateSlot slot) => this.Removals.Any(name => PlaceholderRoleExtensions.TryParseSlotName(name, out var role, out var ordinal) && role == slot.Role && ordinal == slot.Ordinal); }