// Place a rectangle in the current column. Caller must ensure that it fits! private void PlaceInCurrentColumn(IPrintableRectangle rectangle, int startLine, int countLines) { countLines = Math.Min(countLines, rectangle.Boxes.Height - startLine); // if countLines is too big, reduce it. #if DEBUG // Make sure it fits. int numberLinesFit; bool fits = FitsInCurrentColumn(rectangle, startLine, out numberLinesFit); Debug.Assert(fits || countLines <= numberLinesFit); #endif //DEBUG // Add the description to the list of positioned rectangles. PositionedRectangle positionedRectangle; positionedRectangle.description = rectangle; positionedRectangle.pageNumber = currentPage; positionedRectangle.location = new PointF(currentX, currentY); positionedRectangle.startLine = startLine; positionedRectangle.countLines = countLines; positions.Add(positionedRectangle); // Update the variables. if (CurrentColumnEmpty) { columnWidth = rectangle.Boxes.Width * boxSize; } currentY += countLines * boxSize + spacing; if (currentY >= pageSize.Height) { StartNewColumn(); } }
// Layout a single description as many times as you can in one page. Return the number of descriptions // laid out. If the description is too large for one page, 1 is returned and the description is laid out on multiple // pages, so be sure to check PageCount anyway. public int LayoutOneDescriptionPage(IPrintableRectangle rectangle) { int countRectangles = 0; int startLine = 0, countLines; if (!PageSizeBigEnough(new IPrintableRectangle[] { rectangle })) { throw new ApplicationException(MiscText.PageTooSmall); } StartNewPage(); int startPage = currentPage; // Loop until we go the next page. Finish off a description that's in progress. while (currentPage == startPage || startLine != 0) { if (FitsInCurrentColumn(rectangle, startLine, out countLines)) { PlaceInCurrentColumn(rectangle, startLine, countLines); startLine = 0; countRectangles += 1; } else { // It doesn't fit in the current column. if (CurrentColumnEmpty) { if (countLines == 0) { // the description is too wide for the remaining area. StartNewPage(); } else { // The description is too large for one column. Put as much as will fit in one column. PlaceInCurrentColumn(rectangle, startLine, countLines); startLine += countLines; } } else { // The description won't fit in the rest of this column. Start a new one. StartNewColumn(); } } } // If the current page isn't empty and we placed more than one description, remove the last description, // because it shouldn't have been placed. if (!CurrentPageEmpty && countRectangles > 1) { RemoveLastRectangle(); --countRectangles; } return(countRectangles); }
// Determine if the given rectangle (starting at a given line) would fully fit in the current column. // Also returns the number of lines that would fit. private bool FitsInCurrentColumn(IPrintableRectangle rectangle, int startLine, out int numberLinesFit) { numberLinesFit = 0; Size sizeInBoxes = rectangle.Boxes; SizeF size = new SizeF(sizeInBoxes.Width * boxSize, (sizeInBoxes.Height - startLine) * boxSize); // Will width fit? if (CurrentColumnEmpty) { // A column has not been started. Check that width the description <= remaining space on page. if (size.Width > pageSize.Width - currentX) { return(false); } } else { // A column has been started. Check that the width of this description is <= column width if (size.Width > columnWidth) { return(false); } } // Will height fit? float remainingHeight = pageSize.Height - currentY; if (size.Height <= remainingHeight) { // fully fits numberLinesFit = rectangle.Boxes.Height; return(true); } else { // doesn't fully fit. How much does fit? numberLinesFit = (int)(remainingHeight / boxSize); return(false); } }
// Place a rectangle in the current column. Caller must ensure that it fits! private void PlaceInCurrentColumn(IPrintableRectangle rectangle, int startLine, int countLines) { countLines = Math.Min(countLines, rectangle.Boxes.Height - startLine); // if countLines is too big, reduce it. #if DEBUG // Make sure it fits. int numberLinesFit; bool fits = FitsInCurrentColumn(rectangle, startLine, out numberLinesFit); Debug.Assert(fits || countLines <= numberLinesFit); #endif //DEBUG // Add the description to the list of positioned rectangles. PositionedRectangle positionedRectangle; positionedRectangle.description = rectangle; positionedRectangle.pageNumber = currentPage; positionedRectangle.location = new PointF(currentX, currentY); positionedRectangle.startLine = startLine; positionedRectangle.countLines = countLines; positions.Add(positionedRectangle); // Update the variables. if (CurrentColumnEmpty) { columnWidth = rectangle.Boxes.Width * boxSize; } currentY += countLines * boxSize + spacing; if (currentY >= pageSize.Height) StartNewColumn(); }
// Check if page size is big enough to hold any descriptions at all. If not, throw exception. private bool PageSizeBigEnough(IPrintableRectangle[] rectangles) { if (pageSize.Width < boxSize || pageSize.Height < boxSize) return false; foreach (IPrintableRectangle desc in rectangles) { if (desc.Boxes.Width * boxSize > pageSize.Width) return false; } return true; }
// Determine if the given rectangle (starting at a given line) would fully fit in the current column. // Also returns the number of lines that would fit. private bool FitsInCurrentColumn(IPrintableRectangle rectangle, int startLine, out int numberLinesFit) { numberLinesFit = 0; Size sizeInBoxes = rectangle.Boxes; SizeF size = new SizeF(sizeInBoxes.Width * boxSize, (sizeInBoxes.Height - startLine) * boxSize); // Will width fit? if (CurrentColumnEmpty) { // A column has not been started. Check that width the description <= remaining space on page. if (size.Width > pageSize.Width - currentX) return false; } else { // A column has been started. Check that the width of this description is <= column width if (size.Width > columnWidth) return false; } // Will height fit? float remainingHeight = pageSize.Height - currentY; if (size.Height <= remainingHeight) { // fully fits numberLinesFit = rectangle.Boxes.Height; return true; } else { // doesn't fully fit. How much does fit? numberLinesFit = (int) (remainingHeight / boxSize); return false; } }
// Layout a single description as many times as you can in one page. Return the number of descriptions // laid out. If the description is too large for one page, 1 is returned and the description is laid out on multiple // pages, so be sure to check PageCount anyway. public int LayoutOneDescriptionPage(IPrintableRectangle rectangle) { int countRectangles = 0; int startLine = 0, countLines; if (!PageSizeBigEnough(new IPrintableRectangle[] { rectangle })) throw new ApplicationException(MiscText.PageTooSmall); StartNewPage(); int startPage = currentPage; // Loop until we go the next page. Finish off a description that's in progress. while (currentPage == startPage || startLine != 0) { if (FitsInCurrentColumn(rectangle, startLine, out countLines)) { PlaceInCurrentColumn(rectangle, startLine, countLines); startLine = 0; countRectangles += 1; } else { // It doesn't fit in the current column. if (CurrentColumnEmpty) { if (countLines == 0) { // the description is too wide for the remaining area. StartNewPage(); } else { // The description is too large for one column. Put as much as will fit in one column. PlaceInCurrentColumn(rectangle, startLine, countLines); startLine += countLines; } } else { // The description won't fit in the rest of this column. Start a new one. StartNewColumn(); } } } // If the current page isn't empty and we placed more than one description, remove the last description, // because it shouldn't have been placed. if (! CurrentPageEmpty && countRectangles > 1) { RemoveLastRectangle(); --countRectangles; } return countRectangles; }
// Layout one copy of each of multiple descriptions on one or more pages. public void LayoutMultipleDescriptions(IPrintableRectangle[] rectangles) { int startLine = 0, countLines; if (rectangles.Length == 0) return; if (!PageSizeBigEnough(rectangles)) throw new ApplicationException(MiscText.PageTooSmall); List<IPrintableRectangle> rectList = new List<IPrintableRectangle>(rectangles); // Sort by largest width first, then by largest height. rectList.Sort(delegate(IPrintableRectangle d1, IPrintableRectangle d2) { Size size1 = d1.Boxes, size2 = d2.Boxes; if (size1.Width < size2.Width) return 1; else if (size1.Width > size2.Width) return -1; else if (size2.Height != size1.Height) return size2.Height.CompareTo(size1.Height); else return Array.IndexOf(rectangles, d1) - Array.IndexOf(rectangles, d2); }); StartNewPage(); // At each step, we place the largest one that will fit. At a column start, we // place the largest one, period. while (rectList.Count > 0) { // Select a description to place. IPrintableRectangle rectToPlace; if (CurrentColumnEmpty) rectToPlace = rectList[0]; else { rectToPlace = null; foreach (IPrintableRectangle rect in rectList) { if (FitsInCurrentColumn(rect, 0, out countLines)) { rectToPlace = rect; break; } } if (rectToPlace == null) { // Nothing fits. Start a new column. StartNewColumn(); continue; } } // Remove it from the list so we only place it once. rectList.Remove(rectToPlace); // Place it, in multiple pieces if needed. startLine = 0; while (!FitsInCurrentColumn(rectToPlace, startLine, out countLines)) { if (countLines == 0) StartNewPage(); else { PlaceInCurrentColumn(rectToPlace, startLine, countLines); startLine += countLines; } } PlaceInCurrentColumn(rectToPlace, startLine, countLines); } }