namespace AIStudio.Tools.PowerPoint; /// /// One slide layout of a template. /// /// The name the template gives the layout. /// Where the layout is stored inside the file, which identifies it when the presentation is written. /// The layout type as the file spells it, e.g., "title" or "secHead"; empty when the file states none. /// The slots in reading order: from top to bottom, and from left to right within a row. internal sealed record TemplateLayout(string Name, Uri PartUri, string LayoutType, IReadOnlyList Slots) { /// /// Finds the slot a placeholder of a slide based on this layout fills. /// /// /// PowerPoint links the placeholders of a slide to those of its layout by their index, and a /// placeholder without an index, such as the title, by its type. A slide often states the type /// less precisely than its layout does, e.g., none at all for a body, so the index wins. /// /// The placeholder type as the slide spells it; "obj" when it states none. /// The placeholder index the slide states, if any. /// The slot, or null when the placeholder fills none, e.g., because it is the footer. public TemplateSlot? SlotOf(string placeholderType, uint? placeholderIndex) { if (placeholderIndex is { } index && this.Slots.FirstOrDefault(slot => slot.PlaceholderIndex == index) is { } slotByIndex) return slotByIndex; var typeGroup = TypeGroupOf(placeholderType); return this.Slots.FirstOrDefault(slot => (slot.PlaceholderIndex is null || placeholderIndex is null) && TypeGroupOf(slot.PlaceholderType) == typeGroup); } /// /// The type a placeholder is matched by: a title is a title, whether it is centered or not. /// private static string TypeGroupOf(string placeholderType) => placeholderType == "ctrTitle" ? "title" : placeholderType; }