/** * fit the child areas into a box. * Essentially, each child area is given an availible space which is proportional * to its' strength with respect to the overall strength of the overall array. Areas * that can not be re-sized in the width direction (have zero width strenght) simply * get re-sized to thier natural width. * * NOTE: Derived classes must override this method (compound glyphs) because this returns * a new HorzArea, not a derived type. */ public override Area Fit(BoundingBox box) { // the strength and un-adjusted size of the total array. Strength s = Strength; BoundingBox b = BoundingBox; Area[] areas = new Area[content.Length]; // new width of each child area float width; // availible space for exanding filler areas in the width direction // use max here, because width may be negative if we are asked to size into // a smaller area. float availibleSpace = Math.Max(0, box.Width - b.Width); for (int i = 0; i < content.Length; i++) { Strength sa = content[i].Strength; BoundingBox ba = content[i].BoundingBox; if (s.Width == 0 || sa.Width == 0) { // no re-sizing strength, so re-size to natural width width = ba.Width; } else { // re-size to natural width + % of availible space width = ba.Width + availibleSpace * sa.Width / s.Width; } areas[i] = content[i].Fit(BoundingBox.New(width, box.Height, box.Depth)); } return(new HorizontalArea(areas, this)); }
/// <summary> /// Get the minimum formatted area size for the given element using the /// state of the given formatting context. /// /// Currently this just formats the element using the this as the formatter, /// in the future, this will be optimized so that a visitor only calculates /// the min size instead of creating an entire sub tree of areas. /// </summary> public BoundingBox MeasureElement(IFormattingContext ctx, MathMLElement e) { Debug.Assert(ctx.cacheArea == false); if (e != null) { Area a = Area.GetArea(e); if (a != null) { return(a.BoundingBox); } if (cache.Contains(e)) { return((BoundingBox)cache[e]); } else { if (e is MathMLTableElement) { Debug.WriteLine("table element"); } BoundingBox box = (BoundingBox)e.Accept(this, ctx); cache.Add(e, box); return(box); } } else { return(BoundingBox.New()); } }
/** * private ctor used for fit'ing */ private HorizontalArea(Area[] content, Area source) : base(content, source) { // calulate bounding box BoundingBox box = BoundingBox.New(); foreach (Area a in content) { box.Append(a.BoundingBox); } this.box = box; }
public override AreaRegion GetEditRegion(IFormattingContext context, float x, float y, int index) { // measure horizontal position of char: BoundingBox horz = BoundingBox.New(); float left, right; for (int i = 0; i < index && i < content.Length; i++) { BoundingBox tmp = BoundingBox.New(); context.MeasureGlyph(font, content[i], out tmp, out left, out right); horz.Append(tmp); } return(new AreaRegion(this, x + horz.Width, y)); }
/** * re-size all child nodes to the given width, and a height * and depth proportional */ public override Area Fit(BoundingBox box) { // new content area where fitted area are written to Area[] newContent = new Area[content.Length]; // bounding box (natural size), and strength of the array // as a whole BoundingBox b = BoundingBox; Strength s = Strength; // availible height and depth distance to stretch float aHeight = Math.Max(0, box.Height - b.Height); float aDepth = Math.Max(0, box.Depth - b.Depth); for (int i = 0; i < content.Length; i++) { // strength and size of the current child area Strength ps = content[i].Strength; BoundingBox pb = content[i].BoundingBox; // above baseline if (i > baseline && s.Height > 0) { pb.Height += (aHeight * ps.Height) / s.Height; pb.Depth += (aHeight * ps.Depth) / s.Height; } // below baseline else if (i < baseline && s.Depth > 0) { pb.Height += (aDepth * ps.Height) / s.Depth; pb.Depth += (aDepth * ps.Depth) / s.Depth; } // is baseline else if (i == baseline && s.Height + s.Depth > 0) { if (s.Height > 0) { pb.Height += (aHeight * ps.Height) / s.Height; } if (s.Depth > 0) { pb.Depth += (aDepth * ps.Depth) / s.Depth; } } newContent[i] = content[i].Fit(BoundingBox.New(box.Width, pb.Height, pb.Depth)); } return(new VerticalArea(newContent, baseline, this)); }
public BoundingBox[] MeasureElements(IFormattingContext ctx, MathMLElement[] elements) { BoundingBox[] boxes = new BoundingBox[elements.Length]; for (int i = 0; i < elements.Length; i++) { if (elements[i] != null) { boxes[i] = MeasureElement(ctx, elements[i]); } else { boxes[i] = BoundingBox.New(); } } return(boxes); }
/** * create a string area */ public StringArea(IFormattingContext context, string content) { this.font = context.GetFont(); this.content = content; box = BoundingBox.New(); float left = 0, right = 0; for (int i = 0; i < content.Length; i++) { BoundingBox tmp = BoundingBox.New(); context.MeasureGlyph(font, content[i], out tmp, out left, out right); box.Append(tmp); // left edge of first char if (i == 0) { leftEdge = left; } } // right edge of last char rightEdge = right; }
object MathML.MathMLVisitor.Visit(MathMLPresentationContainer e, object args) { IFormattingContext context = ((IFormattingContext)args).Clone(); MathMLNodeList arguments = e.Arguments; BoundingBox extent = BoundingBox.New(); int stretchCount = 0; // save the stretch size because stretch scope can not extend into another // level of nesting BoundingBox stretch = context.Stretch; context.Stretch = BoundingBox.New(); // process all nodes that are not stretchy operators, get thier total // extents for (int i = 0; i < arguments.Count; i++) { MathMLElement element = (MathMLElement)arguments[i]; MathMLOperatorElement op = element as MathMLOperatorElement; if (op == null || op.Stretchy == false) { //areas[i] = (Area)element.Accept(this, context); extent.Append((BoundingBox)element.Accept(this, context)); } if (op != null && op.Stretchy) { stretchCount++; } } // if we have any elements that can be stretched, stretch them if (stretchCount > 0) { if (!stretch.Defined) { // avail width is epsilon because stretchy glyphs were not counted in the // width calculation context.Stretch = BoundingBox.New(Single.Epsilon, extent.Height, extent.Depth); } else { // set the stretch size back to stretch the child elements context.Stretch = stretch; // calculate availible width context.StretchWidth = context.StretchWidth - extent.Width; if (context.Stretch.Width < 0) { context.StretchWidth = 0; } // size to stretch each width equally context.StretchWidth = context.Stretch.Width / (float)stretchCount; } // process all areas that need to be stretched for (int i = 0; i < arguments.Count; i++) { MathMLOperatorElement op = arguments[i] as MathMLOperatorElement; if (op != null && op.Stretchy) { //areas[i] = (Area)op.Accept(this, context); extent.Append((BoundingBox)op.Accept(this, context)); } } } return(extent); }
/** * format a script area. This is an area with a base, and * optional super and subscript areas. */ public static Area Script(IFormattingContext context, Area baseArea, Area subArea, Length subMinShift, Area superArea, Length superMinShift) { // we might have a italic base where we have an overhang at the top, in this // case we need to add a space for the script elements float right = baseArea.RightEdge; // index where to put script elements int scriptIndex; // horz area, length = 2, base + scripts or length = 3: base + space + scripts Area[] horz; float subShift, superShift; if (right < 0) { scriptIndex = 2; horz = new Area[3]; horz[1] = new HorizontalSpaceArea(-right); } else { scriptIndex = 1; horz = new Area[2]; } // set the base horz[0] = baseArea; CalculateScriptShift(context, baseArea.BoundingBox, subArea != null ? subArea.BoundingBox : BoundingBox.New(), subMinShift, superArea != null ? superArea.BoundingBox : BoundingBox.New(), superMinShift, out subShift, out superShift); // the script areas go in an overlap area if (subArea != null && superArea != null) { Area[] overlap = new Area[2]; overlap[0] = Shift(-subShift, subArea); overlap[1] = Shift(superShift, superArea); horz[scriptIndex] = Overlap(overlap); } else if (subArea != null) { horz[scriptIndex] = Shift(-subShift, subArea); } else if (superArea != null) { horz[scriptIndex] = Shift(superShift, superArea); } else { // no script areas, this is not good, but deal with it anyway Debug.WriteLine("Warning, no script areas while create a script area, creating default glyph area"); horz[scriptIndex] = String(context, "?"); } // make a horizontal area out of these return(Horizontal(horz)); }
/** * construct a new sizer. All calculations are done here. */ public MathMLTableSizer(IFormattingContext ctx, MathMLMeasurer measurer, MathMLTableElement table) { try { // copy the context because we change the stretch size so not to // stretch tables ctx = ctx.Clone(); ctx.Stretch = BoundingBox.New(); ctx.cacheArea = false; //MathMLMeasurer measurer = new MathMLMeasurer(); equalColumns = table.EqualColumns; // get the cells from the table and store them in a multi dim array cells = GetCells(table); // min area sizes for the cells BoundingBox[][] minCellSizes = measurer.MeasureElements(ctx, cells); DebugWriteCellSizes(minCellSizes); // count of columns that are actual table cells int cellColumnCount = GetCellColumnCount(cells); // column widths attr from dom Length[] columnWidths = GetColumnWidths(table, cellColumnCount); // column space attr from dom Length[] columnSpacing = GetColumnSpacing(table, cellColumnCount - 1); // frame spaceing attr from dom Length[] frameSpacing = table.FrameSpacing; // row spacing attr from dom Length[] rowSpacing = GetRowSpacing(table, cells.Length - 1); // create a set of columns from the cells columns = CreateColumns(ctx, measurer, cells, minCellSizes, cellColumnCount, columnWidths, columnSpacing, frameSpacing[0]); // create a set of row measurments, this is both the cell rows and spacing rows Row[] rows = CreateRows(ctx, cells, minCellSizes, rowSpacing, frameSpacing[1]); // adjust rows and columns so there is enough space to fit spanning cells AdjustSpanningCells(cells, minCellSizes, rows, columns); Debug.WriteLine("retrieved " + columns.Length + "total columns, "); DebugWriteColumnWidths("Initial Column Widths: "); // get the minimum area for the columns float tableWidth = CalculateTableWidth(ctx, table, columns, equalColumns, cellColumnCount); Debug.WriteLine("Minimum Table Width: " + tableWidth); // find the space needed to fit the availible for the auto and fit columns float availSpace = CalculateColumnAvailSpace(columns, tableWidth); Debug.WriteLine("Availible Space For Columns: " + availSpace); // set the table widths DistributeSpace(columns, tableWidth, availSpace, cellColumnCount, equalColumns); DebugWriteColumnWidths("Column Widths After Formatting: "); // calcuate the shift float shift = 0; CalculateShiftAndSize(ctx, table, rows, columns, ref box, ref shift); // create the cell sizes and offsets CreateCellSizesAndShifts(cells, rows, columns, ref cellSizes, ref cellShifts); // create the lines for the frame and cell separators LineStyle frame = table.Frame; LineStyle[] columnLines = GetColumnLines(table, cellColumnCount); LineStyle[] rowLines = GetRowLines(table, cells.Length); CreateLines(cellSizes, cells, rows, columns, columnLines, rowLines, frame); // set the shift tableShift = GetTableShift(ctx, table, box); } catch (Exception ex) { throw new Exception("Error constructing table sizer: " + ex.Message); } }