public static void GMFrac(MathNode node, XElement output) { DrawBox(node, output); if (node.GetProperty("bevelled") == "true") { DrawTranslatedNode(node.Enumerator, output, 0, node.Enumerator.Height - node.Height); DrawTranslatedNode(node.Denominator, output, node.Width - node.Denominator.Width, node.Depth - node.Denominator.Depth); } else { double enumalign = GetAlign(node, "enumalign"); double denomalign = GetAlign(node, "denomalign"); DrawTranslatedNode(node.Enumerator, output, node.RuleWidth + (node.Width - 2 * node.RuleWidth - node.Enumerator.Width) * enumalign, node.Enumerator.Height - node.Height); DrawTranslatedNode(node.Denominator, output, node.RuleWidth + (node.Width - 2 * node.RuleWidth - node.Denominator.Width) * denomalign, node.Depth - node.Denominator.Depth); } //Draw fraction line double eh; double dh; double ruleX; double ruleY; double x1; double x2; double y1; double y2; if (node.GetProperty("bevelled") == "true") { eh = node.Enumerator.Height + node.Enumerator.Depth; dh = node.Denominator.Height + node.Denominator.Depth; // Determine a point lying on the rule ruleX = (node.Width + node.Enumerator.Width - node.Denominator.Width) / 2.0; if (eh < dh) { ruleY = 0.75 * eh - node.Height; } else { ruleY = node.Depth - 0.75 * dh; } x1 = Math.Max(0, ruleX - (node.Depth - ruleY) / node.Slope); x2 = Math.Min(node.Width, ruleX + (ruleY + node.Height) / node.Slope); y1 = Math.Min(node.Depth, ruleY + ruleX * node.Slope); y2 = Math.Max(-node.Height, ruleY - (node.Width - ruleX) * node.Slope); } else { x1 = 0; y1 = 0; x2 = node.Width; y2 = 0; } DrawLine(output, node.Color, node.RuleWidth, x1, y1, x2, y2, new Dictionary <string, string>() { { "stroke-linecap", "butt" } }); }
public static double GetAlign(MathNode node, string attrName) { string attrValue = node.GetProperty(attrName, "center"); if (alignKeywords.ContainsKey(attrValue)) { return(alignKeywords[attrValue]); } else { return(0.5); //ToDO: node.error("Bad value %s for %s", attrValue, attrName) } }
public static void MakeLimitContext(MathNode node, MathNode child, string accentProperty) { child.DisplayStyle = false; child.TightSpaces = true; string accentValue = node.GetProperty(accentProperty); if (accentValue == null) { accentValue = GetAccentValue(child); } child.Accent = (accentValue == "true"); if (!child.Accent) { child.ScriptLevel += 1; } }
public static void CMo(MathNode node) { // Apply special formatting to operators OperatorStyle extraStyle = node.Config.OperatorStyles.FirstOrDefault(x => x.OpName == node.Text);//.Styling; if (extraStyle != null) { node.Attributes.AddRange(extraStyle.Styling); //ToDo: verify } // Consult the operator dictionary, and set the appropriate defaults string form = "infix"; if (node.Parent == null) { // pass } else if (new List <string>() { "mrow", "mstyle", "msqrt", "merror", "mpadded", "mphantom", "menclose", "mtd", "math" }.Any(x => x == node.Parent.ElementName)) { //ToDo: verify List <MathNode> prevSiblings = node.Parent.Children.TakeWhile((value, i) => i != node.NodeIndex).Where(IsNonSpaceNode).ToList(); List <MathNode> nextSiblings = node.Parent.Children.Skip(node.NodeIndex + 1).Where(IsNonSpaceNode).ToList(); if (prevSiblings.Count == 0 && nextSiblings.Count > 0) { form = "prefix"; } if (nextSiblings.Count == 0 && prevSiblings.Count > 0) { form = "postfix"; } } if (node.Attributes.ContainsKey("form")) { form = node.Attributes["form"]; } node.OpDefaults = node.Config.MathOperators.LookUp(node.Text, form); DefaultContext(node); string stretchyattr = node.GetProperty("stretchy", node.OpDefaults.Dict()["stretchy"]); node.Stretchy = (stretchyattr == "true"); string symmetricattr = node.GetProperty("symmetric", node.OpDefaults.Dict()["symmetric"]); node.Symmetric = (symmetricattr == "true"); node.Scaling = node.OpDefaults.Dict()["scaling"]; if (!node.TightSpaces) { string lspaceattr = node.GetProperty("lspace", node.OpDefaults.Dict()["lspace"]); node.LeftSpace = node.ParseSpace(lspaceattr); string rspaceattr = node.GetProperty("rspace", node.OpDefaults.Dict()["rspace"]); node.RightSpace = node.ParseSpace(rspaceattr); } if (node.DisplayStyle) { string value = node.OpDefaults.Dict()["largeop"]; if (node.GetProperty("largeop", value) == "true") { node.FontSize *= 1.41; } } else { string value = node.OpDefaults.Dict()["movablelimits"]; node.MoveLimits = (node.GetProperty("movablelimits", value) == "true"); } }
public static double GetAlign(MathNode node, string attrName) { string attrValue = node.GetProperty(attrName, "center"); if (alignKeywords.ContainsKey(attrValue)) { return alignKeywords[attrValue]; } else { return 0.5; //ToDO: node.error("Bad value %s for %s", attrValue, attrName) } }
public static void CMTable(MathNode node) { DefaultContext(node); // Display style: no inheritance, default is 'false' unless redefined in 'mstyle' node.DisplayStyle = (node.GetProperty("displaystyle") == "true"); }
public static void MakeLimitContext(MathNode node, MathNode child, string accentProperty) { child.DisplayStyle = false; child.TightSpaces = true; string accentValue = node.GetProperty(accentProperty); if (accentValue == null) accentValue = GetAccentValue(child); child.Accent = (accentValue == "true"); if (!child.Accent) child.ScriptLevel += 1; }
public static void GMFrac(MathNode node, XElement output) { DrawBox(node, output); if (node.GetProperty("bevelled") == "true") { DrawTranslatedNode(node.Enumerator, output, 0, node.Enumerator.Height - node.Height); DrawTranslatedNode(node.Denominator, output, node.Width - node.Denominator.Width, node.Depth - node.Denominator.Depth); } else { double enumalign = GetAlign(node, "enumalign"); double denomalign = GetAlign(node, "denomalign"); DrawTranslatedNode(node.Enumerator, output, node.RuleWidth + (node.Width - 2 * node.RuleWidth - node.Enumerator.Width) * enumalign, node.Enumerator.Height - node.Height); DrawTranslatedNode(node.Denominator, output, node.RuleWidth + (node.Width - 2 * node.RuleWidth - node.Denominator.Width) * denomalign, node.Depth - node.Denominator.Depth); } //Draw fraction line double eh; double dh; double ruleX; double ruleY; double x1; double x2; double y1; double y2; if (node.GetProperty("bevelled") == "true") { eh = node.Enumerator.Height + node.Enumerator.Depth; dh = node.Denominator.Height + node.Denominator.Depth; // Determine a point lying on the rule ruleX = (node.Width + node.Enumerator.Width - node.Denominator.Width) / 2.0; if (eh < dh) ruleY = 0.75 * eh - node.Height; else ruleY = node.Depth - 0.75 * dh; x1 = Math.Max(0, ruleX - (node.Depth - ruleY) / node.Slope); x2 = Math.Min(node.Width, ruleX + (ruleY + node.Height) / node.Slope); y1 = Math.Min(node.Depth, ruleY + ruleX * node.Slope); y2 = Math.Max(-node.Height, ruleY - (node.Width - ruleX) * node.Slope); } else { x1 = 0; y1 = 0; x2 = node.Width; y2 = 0; } DrawLine(output, node.Color, node.RuleWidth, x1, y1, x2, y2, new Dictionary<string, string>() { { "stroke-linecap", "butt" } }); }
public static void CMo(MathNode node) { // Apply special formatting to operators OperatorStyle extraStyle = node.Config.OperatorStyles.Where(x => x.OpName == node.Text).FirstOrDefault();//.Styling; if (extraStyle != null) { node.Attributes.AddRange(extraStyle.Styling); //ToDo: verify } // Consult the operator dictionary, and set the appropriate defaults string form = "infix"; if (node.Parent == null) { // pass } else if (new List<string>() { "mrow", "mstyle", "msqrt", "merror", "mpadded", "mphantom", "menclose", "mtd", "math" }.Any(x => x == node.Parent.ElementName)) { //ToDo: verify List<MathNode> prevSiblings = node.Parent.Children.TakeWhile((value, i) => i != node.NodeIndex).Where(x=> IsNonSpaceNode(x)).ToList(); List<MathNode> nextSiblings = node.Parent.Children.Skip(node.NodeIndex+1).Where(x => IsNonSpaceNode(x)).ToList(); if (prevSiblings.Count == 0 && nextSiblings.Count > 0) form = "prefix"; if (nextSiblings.Count == 0 && prevSiblings.Count > 0) form = "postfix"; } if (node.Attributes.ContainsKey("form")) form = node.Attributes["form"]; node.OpDefaults = node.Config.MathOperators.LookUp(node.Text, form); DefaultContext(node); string stretchyattr = node.GetProperty("stretchy", node.OpDefaults.Dict()["stretchy"]); node.Stretchy = (stretchyattr == "true"); string symmetricattr = node.GetProperty("symmetric", node.OpDefaults.Dict()["symmetric"]); node.Symmetric = (symmetricattr == "true"); node.Scaling = node.OpDefaults.Dict()["scaling"]; if (!node.TightSpaces) { string lspaceattr = node.GetProperty("lspace", node.OpDefaults.Dict()["lspace"]); node.LeftSpace = node.ParseSpace(lspaceattr); string rspaceattr = node.GetProperty("rspace", node.OpDefaults.Dict()["rspace"]); node.RightSpace = node.ParseSpace(rspaceattr); } if (node.DisplayStyle) { string value = node.OpDefaults.Dict()["largeop"]; if (node.GetProperty("largeop", value) == "true") node.FontSize *= 1.41; } else { string value = node.OpDefaults.Dict()["movablelimits"]; node.MoveLimits = (node.GetProperty("movablelimits", value) == "true"); } }
private void Stretch(MathNode node, double? toWidth, double? toHeight, double? toDepth, bool symmetric=false) { if (node == null || !node.Core.Stretchy) return; if (node != node.Base) { if (toWidth != null) toWidth -= node.Width - node.Base.Width; Stretch(node.Base, toWidth, toHeight, toDepth, symmetric); node.MeasureNode(); } else if (node.ElementName == "mo") { if (node.FontSize == 0) return; string maxsizedefault = node.OpDefaults.MaxSize;//["maxsize"]; string maxsizeattr = node.GetProperty("maxsize", maxsizedefault); double? maxScale; if (maxsizeattr == "infinity") { maxScale = null; //Todo: might be invalid. Check original code } else { maxScale = node.ParseSpaceOrPercent(maxsizeattr, node.FontSize, node.FontSize) / node.FontSize; } string minsizedefault = node.OpDefaults.MinSize; string minsizeattr = node.GetProperty("minsize", minsizedefault); double minScale = node.ParseSpaceOrPercent(minsizeattr, node.FontSize, node.FontSize) / node.FontSize; if (toWidth == null) { StretchVertically(node, (double)toHeight, (double)toDepth, minScale, maxScale, symmetric); } else { StretchHorizontally(node, (double)toWidth, minScale, maxScale); } } }
object[] GetAlign(MathNode node) { string alignattr = node.GetProperty("align").Trim(); if (alignattr.Length == 0) alignattr = MathDefaults.globalDefaults["align"]; List<string> splitalign = alignattr.Split(null).ToList(); string aligntype = splitalign[0]; int? alignRow = null; if (splitalign.Count != 1) { alignRow = int.Parse(splitalign[1]); // ToDo: the rest of the python code is not valid. Not implemented here. } return new object[] { aligntype, alignRow }; }
void CalculateRowHeights(MathNode node) { //Set initial row heights for cells with rowspan == 1 double commonAxis = node.Axis(); foreach(RowDescriptor r in node.Rows) { r.Height = 0; r.Depth = 0; foreach (CellDescriptor c in r.Cells) { if (c == null || c.Content == null || c.RowSpan != 1) continue; double cellAxis = c.Content.Axis(); c.VShift = 0; if (c.VAlign == "baseline") { if (r.AlignToAxis == true) c.VShift -= commonAxis; if (c.Content.AlignToAxis == true) c.VShift += cellAxis; } else if (c.VAlign == "axis") { if (!r.AlignToAxis) c.VShift += commonAxis; if (!c.Content.AlignToAxis) c.VShift -= cellAxis; } else { c.VShift = (r.Height - r.Depth - c.Content.Height + c.Content.Depth) / 2; } r.Height = Math.Max(r.Height, c.Content.Height + c.VShift); r.Depth = Math.Max(r.Depth, c.Content.Depth - c.VShift); } } // Calculate heights for cells with rowspan > 1 while (true) { List<RowDescriptor> adjustedRows = new List<RowDescriptor>(); double adjustedSize = 0; foreach (var item in node.Rows.Select((value,i) => new {i, value})) { RowDescriptor r = item.value; foreach (CellDescriptor c in r.Cells) { if (c == null || c.Content == null || c.RowSpan == 1) continue; List<RowDescriptor> rows = node.Rows.Skip(item.i).Take(item.i + c.RowSpan).ToList(); double requiredSize = c.Content.Height + c.Content.Depth; requiredSize -= rows.Where((value, i) => i < rows.Count).Select(x=> x.SpaceAfter).Sum(); double fullSize = rows.Select(x=> x.Height+x.Depth).Sum(); if (fullSize >= requiredSize) continue; double unitSize = requiredSize / rows.Count; while (true) { List<RowDescriptor> oversizedRows = rows.Where(x => x.Height + x.Depth > unitSize).ToList(); //ToDo: Verify if (oversizedRows.Count == 0) break; rows = rows.Where(x => x.Height + x.Depth < unitSize).ToList(); if (rows.Count == 0) break; // weird rounding effects requiredSize -= oversizedRows.Select(x => x.Height + x.Depth).Sum(); unitSize = requiredSize / rows.Count; } if (rows.Count == 0) continue; // protection against arithmetic overflow if (unitSize > adjustedSize) { adjustedSize = unitSize; adjustedRows = rows; } } } if (adjustedRows.Count == 0) break; foreach (RowDescriptor r in adjustedRows) { double delta = (adjustedSize - r.Height - r.Depth) / 2; r.Height += delta; r.Depth += delta; } } if (node.GetProperty("equalrows") == "true") { double maxvsize = node.Rows.Select(x => x.Height + x.Depth).Max(); foreach (RowDescriptor r in node.Rows) { double delta = (maxvsize - r.Height - r.Depth) / 2; r.Height += delta; r.Depth += delta; } } }
void CalculateColumnWidths(MathNode node) { // Get total width string fullwidthattr = "auto"; if (node.Attributes.ContainsKey("width")) fullwidthattr = node.Attributes["width"]; double? fullwidth; if (fullwidthattr == "auto") { fullwidth = null; } else { fullwidth = node.ParseLength(fullwidthattr); if (fullwidth <= 0) fullwidth = null; } // Fill fixed column widths List<string> columnwidths = node.GetListProperty("columnwidth"); foreach (var item in node.Columns.Select((value,i) => new {i, value})) { ColumnDescriptor column = item.value; string attr = GetByIndexOrLast(columnwidths, item.i); if (attr == "auto" || attr == "fit") { column.Fit = (attr == "fit"); } else if (attr.EndsWith("%")) { if (fullwidth == null) { //ToDo: node.error("Percents in column widths supported only in tables with explicit width; width of column %d treated as 'auto'" % (i+1)) } else { double value = 0.0; bool result = double.TryParse(attr.Substring(0, attr.Length - 1), out value); if (result && value > 0) { column.Width = (double)fullwidth * value / 100; column.Auto = false; } } } else { column.Width = node.ParseSpace(attr); column.Auto = true; } } // Set initial auto widths for cells with colspan == 1 foreach (RowDescriptor r in node.Rows) { foreach (var item in r.Cells.Select((value, i)=> new {i, value})) { CellDescriptor c = item.value; if (c == null || c.Content == null || c.ColSpan > 1) continue; ColumnDescriptor column = node.Columns[item.i]; if (column.Auto) column.Width = Math.Max(column.Width, c.Content.Width); } } // Calculate auto widths for cells with colspan > 1 while (true) { List<ColumnDescriptor> adjustedColumns = new List<ColumnDescriptor>(); double adjustedWidth = 0; foreach (RowDescriptor r in node.Rows) { foreach (var item in r.Cells.Select((value,i) => new {i, value})) { CellDescriptor c = item.value; if (c == null || c.Content == null || c.ColSpan == 1) continue; List<ColumnDescriptor> columns = node.Columns.Skip(item.i).Take(item.i + c.ColSpan).ToList(); //ToDo: Verify List<ColumnDescriptor> autoColumns = columns.Where(x => x.Auto == true).ToList(); if (autoColumns.Count == 0) continue; List<ColumnDescriptor> fixedColumns = columns.Where(x=> x.Auto == false).ToList(); double fixedWidth = columns.TakeWhile((value, i) => i != columns.Count-1).Select(x=> x.SpaceAfter).Sum(); if (fixedColumns.Count > 0) fixedWidth += fixedColumns.Select(x => x.Width).Sum(); //ToDo: Verify double autoWidth = autoColumns.Select(x => x.Width).Sum(); //ToDo: Verify if (c.Content.Width <= fixedWidth + autoWidth) continue; // already fits double requiredWidth = c.Content.Width - fixedWidth; double unitWidth = requiredWidth / autoColumns.Count; while (true) { List<ColumnDescriptor> oversizedColumns = autoColumns.Where(x=> x.Width >= unitWidth).ToList(); if (oversizedColumns.Count == 0) break; autoColumns = autoColumns.Where(x => x.Width < unitWidth).ToList(); if (autoColumns.Count == 0) break; //weird rounding effects requiredWidth -= oversizedColumns.Select(x => x.Width).Sum(); unitWidth = requiredWidth / autoColumns.Count; if (autoColumns.Count == 0) continue; //protection against arithmetic overflow //Store the maximum unit width if (unitWidth > adjustedWidth) { adjustedWidth = unitWidth; adjustedColumns = autoColumns; } } } } if (adjustedColumns.Count == 0) break; foreach (ColumnDescriptor col in adjustedColumns) col.Width = adjustedWidth; } if (node.GetProperty("equalcolumns") == "true") { double globalWidth = node.Columns.Where(x => x.Auto = true).Select(x => x.Width).Max(); foreach (ColumnDescriptor col in node.Columns) { if (col.Auto == true) col.Width = globalWidth; } } if (fullwidth != null) { double delta = (double)fullwidth; delta -= node.Columns.Select(x => x.Width).Sum(); delta -= node.Columns.TakeWhile((x, i) => i < node.Columns.Count - 1).Select(x => x.SpaceAfter).Sum(); //ToDo: verify delta -= 2 * node.FrameSpacings[0]; if (delta != 0) { List<ColumnDescriptor> sizableColumns = node.Columns.Where(x => x.Fit == true).ToList(); if (sizableColumns.Count == 0) sizableColumns = node.Columns.Where(x => x.Auto == true).ToList(); if (sizableColumns.Count == 0) { //ToDo: Implement //node.error("Overconstrained table layout: explicit table width specified, but no column has automatic width; table width attribute ignored") } else { delta /= sizableColumns.Count; foreach (ColumnDescriptor col in sizableColumns) col.Width += delta; } } } }
private void MeasureScripts(MathNode node, List<MathNode> subscripts, List<MathNode> superscripts, List<MathNode> presubscripts = null, List<MathNode> presuperscripts = null) { node.SubScripts = subscripts == null ? new List<MathNode>() : subscripts; node.SuperScripts = superscripts == null ? new List<MathNode>() : superscripts; node.PreSubScripts = presubscripts == null ? new List<MathNode>() : presubscripts; node.PreSuperScripts = presuperscripts == null ? new List<MathNode>() : presuperscripts; SetNodeBase(node, node.Children[0]); node.Width = node.Base.Width; node.Height = node.Base.Height; node.Depth = node.Base.Depth; node.Ascender = node.Base.Ascender; node.Descender = node.Base.Descender; List<MathNode> both_subs = new List<MathNode>(node.SubScripts); both_subs.AddRange(node.PreSubScripts); List<MathNode> both_sups = new List<MathNode>(node.SuperScripts); both_sups.AddRange(node.PreSuperScripts); node.SubScriptAxis = both_subs.Select(x => x.Axis()).Union(new List<double>() { 0 }).Max(); node.SuperScriptAxis = both_sups.Select(x => x.Axis()).Union(new List<double>() { 0 }).Max(); List<MathNode> all = new List<MathNode>(both_subs); all.AddRange(both_sups); double gap = all.Select(x => x.NominalLineGap()).Max(); //ToDo:Validate double protrusion = node.ParseLength("0.25ex"); double scriptMedian = node.Axis(); double[] v = GetRowVerticalExtent(both_subs, false, node.SubScriptAxis); double subHeight = v[0]; double subDepth = v[1]; double subAscender = v[2]; double subDescender = v[3]; v = GetRowVerticalExtent(both_sups, false, node.SuperScriptAxis); double superHeight = v[0]; double superDepth = v[1]; double superAscender = v[2]; double superDescender = v[3]; node.SubShift = 0; if (both_subs.Count > 0) { string shiftAttr = node.GetProperty("subscriptshift"); if (shiftAttr == null) shiftAttr = "0.5ex"; node.SubShift = node.ParseLength(shiftAttr); // positive shifts down node.SubShift = Math.Max(node.SubShift, subHeight - scriptMedian + gap); if (node.AlignToAxis) node.SubShift += node.Axis(); node.SubShift = Math.Max(node.SubShift, node.Base.Depth + protrusion - subDepth); node.Height = Math.Max(node.Height, subHeight - node.SubShift); node.Depth = Math.Max(node.Depth, subDepth + node.SubShift); node.Ascender = Math.Max(node.Ascender, subAscender - node.SubShift); node.Descender = Math.Max(node.Descender, subDescender + node.SubShift); } node.SuperShift = 0; if (both_sups.Count > 0) { string shiftAttr = node.GetProperty("superscriptshift"); if (shiftAttr == null) shiftAttr = "1ex"; node.SuperShift = node.ParseLength(shiftAttr); // positive shifts up node.SuperShift = Math.Max(node.SuperShift, superDepth + scriptMedian + gap); if (node.AlignToAxis) node.SuperShift -= node.Axis(); node.SuperShift = Math.Max(node.SuperShift, node.Base.Height + protrusion - superHeight); node.Height = Math.Max(node.Height, superHeight + node.SuperShift); node.Depth = Math.Max(node.Depth, superDepth - node.SuperShift); node.Ascender = Math.Max(node.Ascender, superHeight + node.SuperShift); node.Descender = Math.Max(node.Descender, superDepth - node.SuperShift); } node.PostWidths = ParallelWidths(node.SubScripts, node.SuperScripts); node.PreWidths = ParallelWidths(node.PreSubScripts, node.PreSuperScripts); node.Width += node.PreWidths.Sum() + node.PostWidths.Sum(); }