public FrameworkElement GetPage(int chapterIndex, int pageIndex) { if (pageSize.IsEmpty) { return(null); } if (pageIndex < 0) { return(null); } if (chapterIndex < 0 || chapterIndex > this.Book.Chapters.Count - 1) { return(null); } ChapterInfo chapter = this.Book.Chapters[chapterIndex]; // Check if the last page of the paragraph has already been established, // and the requested page is greater than that last page if (chapter.Pages[chapter.Pages.Count - 1].IsLastPageInChapter && pageIndex > chapter.Pages.Count - 1) { return(null); } // If the last page of a chapter is requested, and it's already available, get it if (pageIndex < chapter.Pages.Count && chapter.Pages[pageIndex].IsLastPageInChapter) { return(BuildPageElement(chapter, chapter.Pages[pageIndex])); } // Otherwise, if the page has already been paginated, get it if (pageIndex + 1 < chapter.Pages.Count && chapter.Pages[pageIndex].IsPaginated) { return(BuildPageElement(chapter, chapter.Pages[pageIndex])); } if (pageIndex != chapter.Pages.Count - 1) { throw new Exception(String.Format("GetPage can only paginate one page per call: pageIndex = {0} while chapter.Pages.Count = {1}", pageIndex, chapter.Pages.Count)); } PageInfo pageInfo = chapter.Pages[pageIndex]; PageInfo nextPageInfo = Paginate(chapter, pageInfo); if (nextPageInfo != null) // ie, if !pageInfo.IsLastPageInChapter { chapter.Pages.Add(nextPageInfo); } return(BuildPageElement(chapter, pageInfo)); }
void GenerateChapters() { // Set so the beginning of the book becomes the first chapter int consecutiveBlankLineCount = 10; int accumulatedCharacterCount = 0; ChapterInfo chapterInfo = null; for (int paragraphIndex = 0; paragraphIndex < paragraphs.Count; paragraphIndex++) { if (paragraphs[paragraphIndex].Text.Length == 0) { consecutiveBlankLineCount += 1; } else { if (consecutiveBlankLineCount > 2) { PageInfo pageInfo = new PageInfo { ParagraphIndex = paragraphIndex, CharacterIndex = 0, AccumulatedCharacterCount = accumulatedCharacterCount }; chapterInfo = new ChapterInfo { Title = paragraphs[paragraphIndex].Text.Length < 100 ? paragraphs[paragraphIndex].Text : paragraphs[paragraphIndex].Text.Substring(0, 100), FirstParagraph = paragraphIndex, ParagraphCount = 1 }; chapterInfo.Pages.Add(pageInfo); this.Book.Chapters.Add(chapterInfo); } else { // Accumulate number of paragraphs in the chapter chapterInfo.ParagraphCount += 1 + consecutiveBlankLineCount; } consecutiveBlankLineCount = 0; accumulatedCharacterCount += paragraphs[paragraphIndex].Text.Length; } } // Flag the last chapter this.Book.Chapters[this.Book.Chapters.Count - 1].IsLastChapter = true; this.Book.CharacterCount = accumulatedCharacterCount; }
FrameworkElement GetLastPage(int chapterIndex, out int pageIndex, bool paginateIfNecessary) { if (pageSize.IsEmpty) { pageIndex = -1; return(null); } if (chapterIndex < 0 || chapterIndex > this.Book.Chapters.Count - 1) { pageIndex = -1; return(null); } ChapterInfo chapter = this.Book.Chapters[chapterIndex]; int lastIndex = chapter.Pages.Count - 1; if (chapter.Pages[lastIndex].IsLastPageInChapter && chapter.Pages[lastIndex].IsPaginated) // IsPaginated should be implied { pageIndex = lastIndex; return(BuildPageElement(chapter, chapter.Pages[lastIndex])); } if (!paginateIfNecessary) { pageIndex = -1; return(null); } while (!chapter.Pages[chapter.Pages.Count - 1].IsLastPageInChapter) { Paginate(chapterIndex, chapter.Pages.Count - 1); } pageIndex = chapter.Pages.Count - 1; return(BuildPageElement(chapter, chapter.Pages[chapter.Pages.Count - 1])); }
// TODO: Could pass chapter and pageIndex here, to obtain pageInfo within, and then can set nextPageInfo PageInfo Paginate(ChapterInfo chapter, PageInfo pageInfo) { // Get dimensions of space character Size spaceSize = fontMetrics.MeasureText(" "); int lineHeight = (int)Math.Ceiling(fontSize * spaceSize.Height); int spaceWidth = (int)Math.Ceiling(fontSize * spaceSize.Width); // Initialize a couple variables int accumHeight = 0; int characterCount = pageInfo.AccumulatedCharacterCount; PageInfo nextPageInfo = null; int paragraphIndex = pageInfo.ParagraphIndex; int characterIndex = pageInfo.CharacterIndex; // Loop for lines on page while (true) { ParagraphInfo paragraph = paragraphs[paragraphIndex]; // For blank paragraphs, leave a gap... if (paragraph.Text.Length == 0) { // .. but not when it's at the top of the page if (accumHeight > 0) accumHeight += (int)Math.Ceiling(ParagraphSpacing * lineHeight); } // Normal case for non-blank paragraphs else { // This is the amount to indent the entire paragraph int indent = spaceWidth * paragraph.Indent; // Apply first line indent if paragraph indent is zero, and it's // not the first paragraph in the chapter if (indent == 0 && characterIndex == 0 && paragraph != paragraphs[chapter.FirstParagraph]) { indent = spaceWidth * FirstLineIndent; } // Start off accumulated line width with the indent int accumWidth = indent; // Loop for words within line -- always the same paragraph while (true) { // Find next space in paragraph int spaceIndex = paragraph.Text.IndexOf(' ', characterIndex); int wordCharacters = (spaceIndex == -1 ? paragraph.Text.Length : spaceIndex) - characterIndex; int wordWidth = (int)Math.Ceiling(fontSize * fontMetrics.MeasureText(paragraph.Text, characterIndex, wordCharacters).Width); // Check if the accumulated width exceeds the page width, // but make sure to have at least one word. if (accumWidth > indent && accumWidth + wordWidth >= pageSize.Width) break; WordInfo word = new WordInfo { LocationLeft = accumWidth, LocationTop = accumHeight, ParagraphIndex = paragraphIndex, CharacterIndex = characterIndex, CharacterCount = wordCharacters, }; pageInfo.Words.Add(word); accumWidth += wordWidth; characterCount += wordCharacters + (spaceIndex == -1 ? 0 : 1); characterIndex += wordCharacters + (spaceIndex == -1 ? 0 : 1); // End of line because it's the end of the paragraph if (spaceIndex == -1) { characterIndex = 0; break; } // End of line because no more words will fit if (accumWidth + spaceWidth >= pageSize.Width) break; accumWidth += spaceWidth; // Otherwise, point to next word characterIndex = spaceIndex + 1; // ready for next word } // Bump up the accumulated height of the page accumHeight += lineHeight; } // Start a new line, but check first if it's a new paragraph if (characterIndex == 0) { // If we've been working with the last paragraph in the chapter, // we're also done with the page if (paragraphIndex + 1 == chapter.FirstParagraph + chapter.ParagraphCount) { pageInfo.IsLastPageInChapter = true; break; } // Otherwise, kick up the paragraphIndex paragraphIndex++; } // Check if the next paragraph is too much for this page if ((paragraphs[paragraphIndex].Text.Length == 0 && accumHeight + lineHeight / 2 > pageSize.Height) || accumHeight + lineHeight > pageSize.Height) { break; } } pageInfo.IsPaginated = true; // Create PageInfo for next page if (!pageInfo.IsLastPageInChapter) { nextPageInfo = new PageInfo { ParagraphIndex = paragraphIndex, CharacterIndex = characterIndex, AccumulatedCharacterCount = characterCount }; } return nextPageInfo; }
void GenerateChapters() { // Set so the beginning of the book becomes the first chapter int consecutiveBlankLineCount = 10; int accumulatedCharacterCount = 0; ChapterInfo chapterInfo = null; for (int paragraphIndex = 0; paragraphIndex < paragraphs.Count; paragraphIndex++) { if (paragraphs[paragraphIndex].Text.Length == 0) { consecutiveBlankLineCount += 1; } else { if (consecutiveBlankLineCount > 2) { PageInfo pageInfo = new PageInfo { ParagraphIndex = paragraphIndex, CharacterIndex = 0, AccumulatedCharacterCount = accumulatedCharacterCount }; chapterInfo = new ChapterInfo { Title = paragraphs[paragraphIndex].Text.Length < 100 ? paragraphs[paragraphIndex].Text : paragraphs[paragraphIndex].Text.Substring(0, 100), FirstParagraph = paragraphIndex, ParagraphCount = 1 }; chapterInfo.Pages.Add(pageInfo); this.Book.Chapters.Add(chapterInfo); } else { // Accumulate number of paragraphs in the chapter chapterInfo.ParagraphCount += 1 + consecutiveBlankLineCount; } consecutiveBlankLineCount = 0; accumulatedCharacterCount += paragraphs[paragraphIndex].Text.Length; } } // Flag the last chapter this.Book.Chapters[this.Book.Chapters.Count - 1].IsLastChapter = true; this.Book.CharacterCount = accumulatedCharacterCount; }
FrameworkElement BuildPageElement(ChapterInfo chapter, PageInfo pageInfo) { if (pageInfo.Words.Count == 0) { Paginate(chapter, pageInfo); } Canvas canvas = new Canvas(); foreach (WordInfo word in pageInfo.Words) { TextBlock txtblk = new TextBlock { FontFamily = fontMetrics.Font.FontFamily, FontSize = this.fontSize, Text = paragraphs[word.ParagraphIndex].Text. Substring(word.CharacterIndex, word.CharacterCount), Tag = word }; Canvas.SetLeft(txtblk, word.LocationLeft); Canvas.SetTop(txtblk, word.LocationTop); canvas.Children.Add(txtblk); } return canvas; }
// TODO: Could pass chapter and pageIndex here, to obtain pageInfo within, and then can set nextPageInfo PageInfo Paginate(ChapterInfo chapter, PageInfo pageInfo) { // Get dimensions of space character Size spaceSize = fontMetrics.MeasureText(" "); int lineHeight = (int)Math.Ceiling(fontSize * spaceSize.Height); int spaceWidth = (int)Math.Ceiling(fontSize * spaceSize.Width); // Initialize a couple variables int accumHeight = 0; int characterCount = pageInfo.AccumulatedCharacterCount; PageInfo nextPageInfo = null; int paragraphIndex = pageInfo.ParagraphIndex; int characterIndex = pageInfo.CharacterIndex; // Loop for lines on page while (true) { ParagraphInfo paragraph = paragraphs[paragraphIndex]; // For blank paragraphs, leave a gap... if (paragraph.Text.Length == 0) { // .. but not when it's at the top of the page if (accumHeight > 0) { accumHeight += (int)Math.Ceiling(ParagraphSpacing * lineHeight); } } // Normal case for non-blank paragraphs else { // This is the amount to indent the entire paragraph int indent = spaceWidth * paragraph.Indent; // Apply first line indent if paragraph indent is zero, and it's // not the first paragraph in the chapter if (indent == 0 && characterIndex == 0 && paragraph != paragraphs[chapter.FirstParagraph]) { indent = spaceWidth * FirstLineIndent; } // Start off accumulated line width with the indent int accumWidth = indent; // Loop for words within line -- always the same paragraph while (true) { // Find next space in paragraph int spaceIndex = paragraph.Text.IndexOf(' ', characterIndex); int wordCharacters = (spaceIndex == -1 ? paragraph.Text.Length : spaceIndex) - characterIndex; int wordWidth = (int)Math.Ceiling(fontSize * fontMetrics.MeasureText(paragraph.Text, characterIndex, wordCharacters).Width); // Check if the accumulated width exceeds the page width, // but make sure to have at least one word. if (accumWidth > indent && accumWidth + wordWidth >= pageSize.Width) { break; } WordInfo word = new WordInfo { LocationLeft = accumWidth, LocationTop = accumHeight, ParagraphIndex = paragraphIndex, CharacterIndex = characterIndex, CharacterCount = wordCharacters, }; pageInfo.Words.Add(word); accumWidth += wordWidth; characterCount += wordCharacters + (spaceIndex == -1 ? 0 : 1); characterIndex += wordCharacters + (spaceIndex == -1 ? 0 : 1); // End of line because it's the end of the paragraph if (spaceIndex == -1) { characterIndex = 0; break; } // End of line because no more words will fit if (accumWidth + spaceWidth >= pageSize.Width) { break; } accumWidth += spaceWidth; // Otherwise, point to next word characterIndex = spaceIndex + 1; // ready for next word } // Bump up the accumulated height of the page accumHeight += lineHeight; } // Start a new line, but check first if it's a new paragraph if (characterIndex == 0) { // If we've been working with the last paragraph in the chapter, // we're also done with the page if (paragraphIndex + 1 == chapter.FirstParagraph + chapter.ParagraphCount) { pageInfo.IsLastPageInChapter = true; break; } // Otherwise, kick up the paragraphIndex paragraphIndex++; } // Check if the next paragraph is too much for this page if ((paragraphs[paragraphIndex].Text.Length == 0 && accumHeight + lineHeight / 2 > pageSize.Height) || accumHeight + lineHeight > pageSize.Height) { break; } } pageInfo.IsPaginated = true; // Create PageInfo for next page if (!pageInfo.IsLastPageInChapter) { nextPageInfo = new PageInfo { ParagraphIndex = paragraphIndex, CharacterIndex = characterIndex, AccumulatedCharacterCount = characterCount }; } return(nextPageInfo); }