/// <summary> /// Split this layout at the position determined during an earlier call to /// AssessPageBreak. Moves sublayouts below the split into a new layout /// and returns that new layout. /// </summary> public override Layout DoPageBreak() { using (new TraceContextPusher(_generator, _traceContext)) { Trace("split index {0}", _pageSplitIndex); // If we come here then we're either splitting or moving entirely to // the next page. This situation could be caused by either the bullet // or the content being too long to fit on the current page. We never // split the bullet, and the bullet and content are always top-aligned, // so if it's the bullet that's too long then we want to move entirely // to the next page, regardless of the length of the content. In either // case we want to split the content (possibly leaving the first half // of the split empty) and duplicate the bullet (leaving either the // original or the copy empty). // Create a copy of ourself ListItemLayout lower = (ListItemLayout)ShallowCopy(); // Split our content and move the lower half to the copy Layout contentCopy = _contentLayout.DoPageBreak(); lower.AddSubLayout(contentCopy); lower._contentLayout = contentCopy; // If the content was moved entirely to the next page, leaving the // original content layout empty, then the copy needs a valid and fully // functional bullet. If the content was split then the copy doesn't // need a visible bullet, and shouldn't be capable of being renumbered, // but everything's a lot easier if we give it an empty bullet layout // anyway. Layout bulletCopy = null; if (!_contentLayout.IsEmpty()) // content was split { bulletCopy = new TextLayout("", null, _generator, _trackingInfo.LineNumber, _trackingInfo.LinePosition); } else { bulletCopy = _bulletLayout.DeepCopy(); } lower.AddSubLayout(bulletCopy); lower._bulletLayout = bulletCopy; return(lower); } }
public override void RemoveEmptyLayouts() { base.RemoveEmptyLayouts(); // Renumber our list items to fill in any gaps left by removed items int number = _style.BulletStyle.StartAt; foreach (Layout layout in _subLayouts) { if (!layout.IsEmpty()) { ListItemLayout item = layout as ListItemLayout; // If a list item has been split across a page break then we // only want to renumber its upper half if (item.HasBullet) { item.Renumber(number++); } } } }
/// <summary> /// Wrap all our sublayouts in ListItemLayouts. /// </summary> private List <Layout> WrapSublayouts(List <Layout> unwrapped) { List <Layout> wrapped = new List <Layout>(); int number = _style.BulletStyle.StartAt; foreach (Layout layout in unwrapped) { ListItemLayout item = null; if (layout is ListItemLayout) { item = (ListItemLayout)layout; // already wrapped } else { item = new ListItemLayout(layout, _style, number++, _generator, _trackingInfo.LineNumber, _trackingInfo.LinePosition, _traceContext); } wrapped.Add(item); } return(wrapped); }
/// <summary> /// Evalute page break rules for within a single item in the list. /// </summary> private bool EvaluateItemPageBreakRules(Position position, ListItemLayout item, PageLayout pageLayout) { // If we're outside the page's body box then we're not body text, and // so we don't apply page break rules if (!pageLayout.BodyBox.Contains(position)) { return(false); } // If we're already on a new page then there's nothing to do. Even if // our rules said that we don't have enough room, we can't fix that // by creating another page. if (position.Y >= pageLayout.BodyBox.Top) { return(false); } // If we have our own rules then use them, otherwise if the current // page has rules the use them, otherwise use none PageBreakRules rules = item.PageBreakRules; if (rules == null) { rules = pageLayout.PageBreakRules; } if (rules == null) { rules = _defaultItemPageBreakRules; } //-------------------------------------------------------- // 1. New-page rule if (rules.NewPage) { return(true); } //-------------------------------------------------------- // 2. Keep-with-next rule //-------------------------------------------------------- // 3. Max-position rule // Our current position is an offset from the bottom of the page, not // from the bottom of the body box, so subtract the body box bottom // to recalibrate the calculations to start at zero. int y = position.Y - pageLayout.BodyBox.Bottom; // Our position is measured from the bottom, but the rule is expressed // in terms where zero is at the top, so invert position. y = pageLayout.BodyBox.Height - y; // Convert the position to a percentage float percent = (float)y / (float)pageLayout.BodyBox.Height; // Now check the rule if (percent > rules.MaximumPosition) { return(true); } //-------------------------------------------------------- // 4. Min-lines rule // Work out the height we need to draw our minimum start lines. // If it's greater than the space available on the page then // start a new page. // int minLines = item.Lines.Count > rules.MinimumLines ? rules.MinimumLines : item.Lines.Count; // int minStartHeight = 0; // for(int x = 0; x < minLines; ++x) // { // int lineHeight = item.Lines[x].Height; // minStartHeight += lineHeight; // } // if(position.Y < pageLayout.BodyBox.Bottom + (_style.Padding?.Bottom ?? 0) + (_style.ItemStyle.Padding?.Bottom ?? 0) + minStartHeight) // return true; //-------------------------------------------------------- // No rule said that we need a break, so stay on the current page return(false); }
private void RenderListItem(ListItemLayout layout, IContentContainer container) { // A list item, which is based on a single design layout, can // include any number of design paragraphs introduced by embedded // HTML <p> tags. To render these as a single item in the Word // list, they must all be rendered as a single Word paragraph. // We achieve this by concatenating all the design paragraphs // together, with double line break separators. The first line // break starts a new line, and the second inserts some whitespace. // // A nested list is introduced by a group layout, which is a single // item in the current list and which contains its own list. // // We only support text content in list items - no photos. // // So the list item layout's content sublayout is always either // a text layout or a group layout. And the list item is always // a single Word paragraph. // The item style is to be found in the item's list's style s.ListStyle listStyle = (s.ListStyle)layout.Style; s.TextStyle itemStyle = listStyle.ItemStyle; IParagraph paragraph = container.AddParagraph(itemStyle, layout.TrackingInfo); int numberingInstanceId = _numberingInstances.Peek(); // stack guaranteed not to be empty paragraph.SetNumbering(numberingInstanceId, _numberingLevel); switch (layout.ContentLayout.LayoutType) { case LayoutType.Text: { TextLayout contentLayout = (TextLayout)layout.ContentLayout; List <Verse> verses = contentLayout.GetFormattedText(); if (verses == null) { break; } if (verses.Count == 0) { break; } foreach (Verse verse in verses) { if (verse is ParagraphVerse) { // Two line breaks to look like a new paragraph without actually // being a new Word paragraph. But don't do this if it's the first // verse. if (verse == verses[0]) { continue; } paragraph.AddLineBreak(); paragraph.AddLineBreak(); } else if (verse is LineBreakVerse) { paragraph.AddLineBreak(); } else { paragraph.AddRun(verse.Text, verse.Format.Font, verse.Format.Color); } } break; } case LayoutType.Group: { foreach (Layout sublayout in layout.ContentLayout.SubLayouts) { Render(sublayout, container); } break; } default: { break; } } }
private void RenderListItemLayout(ListItemLayout layout, Page page) { Render(layout.BulletLayout, page); Render(layout.ContentLayout, page); }
public ListItemLayout(ListItemLayout src) : base(src) { _number = src._number; _listStyle = src._listStyle; }