/// Checks a slide deck the model wrote against the template it was written for.
/// </summary>
/// <remarks>
/// Every issue is written for the model: the Slide Assistant sends the issues back in a repair
/// round, so each one names the slide and the slot, and says what to do instead. What fits the
/// template is checked here and nowhere else, so that the writer of the file can rely on it.
/// </remarks>
internalstaticclassSlideDeckSpecValidator
{
/// <summary>
/// How far the estimated text may exceed the estimated capacity of a slot; at least one line more
/// is always accepted.
/// </summary>
/// <remarks>
/// Both are rough estimates, and the template shrinks text which runs a little long. Only text
/// clearly beyond the slot is sent back. The extra line keeps a two-line title in a box made for
/// one from being rejected, which PowerPoint simply sets a little smaller.
/// </remarks>
privateconstdoubleCAPACITY_TOLERANCE=1.2;
/// <summary>
/// A table column narrower than this many characters is hardly readable.
/// </summary>
privateconstintMIN_COLUMN_CHARACTERS=8;
/// <summary>
/// Each list level indents a paragraph by about this many characters.
/// </summary>
privateconstintINDENT_CHARACTERS_PER_LEVEL=4;
/// <summary>
/// Checks a slide deck.
/// </summary>
/// <param name="spec">The slide deck.</param>
/// <param name="template">The template whose layout families the deck uses.</param>
/// <param name="pictureCount">How many pictures the user attached for the slides, which the slides refer to by number; each goes on exactly one slide.</param>
/// <param name="expectedSlideCount">How many slides the user asked for, or 0 when the model may choose.</param>
/// <param name="templateSlideUses">What the user chose for each slide of the template, by its number; null or empty when the deck uses no template slides.</param>
/// <param name="chosenLayouts">The layouts the user chose for kinds of slides; null when the model decides every layout.</param>
/// <param name="maxMainPointsPerSlide">How many main points a slide may have at most, all its texts together, or 0 when the model may choose.</param>
/// <param name="libraryPictures">The pictures of a picture library the slides may use, numbered after the pictures for the slides; null for no library.</param>
/// <returns>The issues, written for the model; empty when the deck fits.</returns>
issues.Add($"Slide {number} puts icon {picture} into picture {slot.Ordinal}, which is a backdrop. Put icons only into picture slots which are no backdrop, or choose another picture.");
issues.Add($"Picture {picture} of PICTURE_LIBRARY is on slides {string.Join(",", slides)}. Use a picture of the library at most once; only icons may repeat.");
}
}
/// <summary>
/// Checks that every picture the user attached for the slides is on exactly one slide.
/// </summary>
/// <remarks>
/// The user attached these pictures for the presentation to show them, once each. A template
/// without a single picture slot leaves no place for them, which no repair round can change.
issues.Add($"Slide {number} has {mainPoints} main points, but at most {maxMainPoints} per slide were asked for, all its texts together. Merge or shorten them, or move some to another slide.");
}
/// <summary>
/// Says whether a slide can be written at all.
/// </summary>
/// <remarks>
/// A slide on a layout the template does not have, or one referring to a template slide the user
/// did not choose, cannot be written. Any other issue, such as a text which runs long, leaves a
/// slide worth writing and looking at.
/// </remarks>
/// <param name="slide">The slide.</param>
/// <param name="template">The template whose layout families and slides the deck uses.</param>
/// <param name="templateSlideUses">What the user chose for each slide of the template, by its number.</param>
issues.Add($"Slide {number} is a new slide, which has nothing to remove. Leave out \"remove\".");
varfamily=template.FindFamily(slide.Layout);
if(familyisnull)
{
issues.Add($"Slide {number} uses the layout \"{slide.Layout}\", which does not exist. Use one of {string.Join(",", template.Families.Select(candidate => candidate.Id))}.");
issues.Add($"Slide {number} starts a chapter, but uses layout {family.Id}. Chapters start with a slide on layout {chapterFamily.Id}; put their content on the slides that follow.");
}
/// <summary>
/// Checks a slide which refers to a template slide: a kept one takes no content, an edited one
/// needs some, and it has to fit the slots of the template slide where the slide puts them.
?$"Slide {number} refers to template slide {templateSlideNumber}, which is not offered. Refer only to template slides {string.Join(",", offered)}."
:$"Slide {number} refers to template slide {templateSlideNumber}, but no template slides are offered. Leave out \"template_slide\".");
return;
}
if(useisTemplateSlideUse.KEEP)
{
if(slide.HasContent)
issues.Add($"Slide {number} keeps template slide {templateSlideNumber} unchanged, so it takes no content. Write only \"template_slide\", and \"starts_chapter\" where it opens a chapter.");
return;
}
if(!slide.HasContent)
{
issues.Add($"Slide {number} is to edit template slide {templateSlideNumber}, but writes no content. Write the new content of the slide, or what it removes.");
return;
}
if(template.FamilyOf(slide)isnot{}family)
{
issues.Add($"Template slide {templateSlideNumber} has no slots to fill. Keep it unchanged: write only \"template_slide\".");
issues.Add($"Slide {number} removes \"{name}\", which template slide {templateSlide.Number} does not have. Remove only the slots and elements its entry in TEMPLATE_SLIDES names, e.g., \"element 1\" or \"label 1\".");
continue;
}
if(roleisPlaceholderRole.TITLE)
issues.Add($"Slide {number} removes the title of template slide {templateSlide.Number}. Write a new title instead, or keep the old one.");
elseif(slide.Fills(role,ordinal))
issues.Add($"Slide {number} fills {name} and removes it as well. Either fill it or remove it.");
}
}
/// <summary>
/// Checks that every template slide the user chose appears exactly once, in the order of the
/// template.
/// </summary>
/// <remarks>
/// References to slides the user did not choose are reported with the slide which makes them.
?$"Template slide {number} is missing. Add it at its place as {{ \"template_slide\": {number} }}."
:$"Template slide {number} is missing. Add it at its place with \"template_slide\": {number} and its new content.");
}
elseif(count>1)
issues.Add($"Template slide {number} appears {count} times. Use every template slide exactly once.");
}
varorder=references.Distinct().ToList();
if(!order.SequenceEqual(order.Order()))
issues.Add($"The template slides are out of order. Keep them in the order {string.Join(",", order.Order())}, and put new slides before, between, or after them.");
}
/// <summary>
/// The numbers of the template slides the user chose to keep or to edit, in the order of the template.