public void CreateCharacters(Atom atom, Options options) { string module = $"{_product}.{_class}.{MethodBase.GetCurrentMethod().Name}()"; //Debug.WriteLine("Atom: " + atom.Id + " is " + atom.ElementType); //Point atomCentre = new Point((double)atom.X2, (double)atom.Y2); string atomLabel = atom.Element.Symbol; Rect labelBounds; //Debug Code //atomLabel = "H" + "abcdefHghijklmnopqrstuvwxyz" + "H"; //atomLabel = "ABCHDEFGHIJKLMNOHPQRSHTUVWXYZ"; //atomLabel = "-+H01234567890H+-"; // Get Charge and Isotope valuesfor use later on int iCharge = atom.FormalCharge ?? 0; int iAbsCharge = Math.Abs(iCharge); int isoValue = atom.IsotopeNumber ?? 0; // Get Implicit Hydrogen Count for use later on int implicitHCount = atom.ImplicitHydrogenCount; Point cursorPosition = atom.Position; Point chargeCursorPosition = atom.Position; Point isotopeCursorPosition = atom.Position; double lastOffset = 0; //Debug.WriteLine(" X: " + atom.X2 + " Y: " + atom.Y2 + " Implicit H Count: " + implicitHCount); int ringCount = atom.Rings.Count(); int bondCount = atom.Bonds.Count; //var bv = atom.BalancingVector; //_telemetry.Write(module, "Debugging", $"Atom {atomLabel} [{atom.Id}] at {atom.Position} BalancingVector {bv} [{CoordinateTool.BearingOfVector(bv)}°]"); #region Decide if atom label is to be displayed bool showLabel = true; if (atomLabel == "C") { if (!options.ShowCarbons) { if (ringCount > 0 || bondCount > 1) { showLabel = false; } if (bondCount == 2) { Point p1 = atom.Bonds[0].OtherAtom(atom).Position; Point p2 = atom.Bonds[1].OtherAtom(atom).Position; double angle1 = Vector.AngleBetween(-(atom.Position - p1), atom.Position - p2); if (Math.Abs(angle1) < 8) { showLabel = true; } } // Force on if atom has charge if (iAbsCharge > 0) { showLabel = true; } // Force on if atom has isotope value if (isoValue > 0) { showLabel = true; } } } #endregion Decide if atom label is to be displayed if (showLabel) { #region Set Up Atom Colours string atomColour = "000000"; if (options.ColouredAtoms) { if ((atom.Element as Element).Colour != null) { atomColour = ((Element)atom.Element).Colour; // Strip out # as OoXml does not use it atomColour = atomColour.Replace("#", ""); } } #endregion Set Up Atom Colours #region Step 1 - Measure Bounding Box for all Characters of label double xMin = double.MaxValue; double yMin = double.MaxValue; double xMax = double.MinValue; double yMax = double.MinValue; Point thisCharacterPosition; for (int idx = 0; idx < atomLabel.Length; idx++) { char chr = atomLabel[idx]; TtfCharacter c = m_TtfCharacterSet[chr]; if (c != null) { thisCharacterPosition = GetCharacterPosition(cursorPosition, c); xMin = Math.Min(xMin, thisCharacterPosition.X); yMin = Math.Min(yMin, thisCharacterPosition.Y); xMax = Math.Max(xMax, thisCharacterPosition.X + OoXmlHelper.ScaleCsTtfToCml(c.Width)); yMax = Math.Max(yMax, thisCharacterPosition.Y + OoXmlHelper.ScaleCsTtfToCml(c.Height)); // Uncomment the following to disable offsetting for terminal atoms //if (bonds.Count == 1) //{ // break; //} if (idx < atomLabel.Length - 1) { // Move to next Character position cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX), 0); } } } #endregion Step 1 - Measure Bounding Box for all Characters of label #region Step 2 - Reset Cursor such that the text is centered about the atom's co-ordinates double width = xMax - xMin; double height = yMax - yMin; cursorPosition = new Point(atom.Position.X - width / 2, atom.Position.Y + height / 2); chargeCursorPosition = new Point(cursorPosition.X, cursorPosition.Y); isotopeCursorPosition = new Point(cursorPosition.X, cursorPosition.Y); labelBounds = new Rect(cursorPosition, new Size(width, height)); //_telemetry.Write(module, "Debugging", $"Atom {atomLabel} [{atom.Id}] Label Bounds {labelBounds}"); #endregion Step 2 - Reset Cursor such that the text is centered about the atom's co-ordinates #region Step 3 - Place the characters foreach (char chr in atomLabel) { TtfCharacter c = m_TtfCharacterSet[chr]; if (c != null) { thisCharacterPosition = GetCharacterPosition(cursorPosition, c); AtomLabelCharacter alc = new AtomLabelCharacter(thisCharacterPosition, c, atomColour, chr, atom.Id); m_AtomLabelCharacters.Add(alc); // Move to next Character position lastOffset = OoXmlHelper.ScaleCsTtfToCml(c.IncrementX); cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX), 0); chargeCursorPosition = new Point(cursorPosition.X, cursorPosition.Y); } } #endregion Step 3 - Place the characters #region Determine NESW double baFromNorth = Vector.AngleBetween(BasicGeometry.ScreenNorth(), atom.BalancingVector); CompassPoints nesw = CompassPoints.East; if (bondCount == 1) { nesw = BasicGeometry.SnapTo2EW(baFromNorth); } else { nesw = BasicGeometry.SnapTo4NESW(baFromNorth); } #endregion Determine NESW #region Step 4 - Add Implicit H if required if (options.ShowHydrogens && implicitHCount > 0) { TtfCharacter hydrogenCharacter = m_TtfCharacterSet['H']; string numbers = "012345"; TtfCharacter implicitValueCharacter = m_TtfCharacterSet[numbers[implicitHCount]]; #region Add H if (hydrogenCharacter != null) { switch (nesw) { case CompassPoints.North: if (atom.Bonds.Count > 1) { cursorPosition.X = labelBounds.X + (labelBounds.Width / 2) - (OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Width) / 2); cursorPosition.Y = cursorPosition.Y + OoXmlHelper.ScaleCsTtfToCml(-hydrogenCharacter.Height) - OoXmlHelper.CHARACTER_CLIPPING_MARGIN; if (iCharge > 0) { if (implicitHCount > 1) { cursorPosition.Offset(0, OoXmlHelper.ScaleCsTtfToCml(-implicitValueCharacter.Height * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR / 2) - OoXmlHelper.CHARACTER_CLIPPING_MARGIN); } } } break; case CompassPoints.East: // Leave as is break; case CompassPoints.South: if (atom.Bonds.Count > 1) { cursorPosition.X = labelBounds.X + (labelBounds.Width / 2) - (OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Width) / 2); cursorPosition.Y = cursorPosition.Y + OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Height) + OoXmlHelper.CHARACTER_CLIPPING_MARGIN; } break; case CompassPoints.West: if (implicitHCount == 1) { cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(-(hydrogenCharacter.IncrementX * 2)), 0); } else { cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(-(hydrogenCharacter.IncrementX * 2.5)), 0); } break; } //_telemetry.Write(module, "Debugging", $"Adding H at {cursorPosition}"); thisCharacterPosition = GetCharacterPosition(cursorPosition, hydrogenCharacter); AtomLabelCharacter alc = new AtomLabelCharacter(thisCharacterPosition, hydrogenCharacter, atomColour, 'H', atom.Id); m_AtomLabelCharacters.Add(alc); // Move to next Character position cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.IncrementX), 0); if (nesw == CompassPoints.East) { chargeCursorPosition = new Point(cursorPosition.X, cursorPosition.Y); } if (nesw == CompassPoints.West) { isotopeCursorPosition = new Point(thisCharacterPosition.X, isotopeCursorPosition.Y); } } #endregion Add H #region Add number if (implicitHCount > 1) { if (implicitValueCharacter != null) { thisCharacterPosition = GetCharacterPosition(cursorPosition, implicitValueCharacter); // Drop the subscript Character thisCharacterPosition.Offset(0, OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Width * OoXmlHelper.SUBSCRIPT_DROP_FACTOR)); AtomLabelCharacter alc = new AtomLabelCharacter(thisCharacterPosition, implicitValueCharacter, atomColour, numbers[implicitHCount], atom.Id); alc.IsSubScript = true; m_AtomLabelCharacters.Add(alc); // Move to next Character position cursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(implicitValueCharacter.IncrementX) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0); } } #endregion Add number } #endregion Step 4 - Add Implicit H if required #region Step 5 - Add Charge if required if (iCharge != 0) { TtfCharacter hydrogenCharacter = m_TtfCharacterSet['H']; char sign = '.'; TtfCharacter chargeSignCharacter = null; if (iCharge >= 1) { sign = '+'; chargeSignCharacter = m_TtfCharacterSet['+']; } else if (iCharge <= 1) { sign = '-'; chargeSignCharacter = m_TtfCharacterSet['-']; } if (iAbsCharge > 1) { string digits = iAbsCharge.ToString(); // Insert digits foreach (char chr in digits) { TtfCharacter chargeValueCharacter = m_TtfCharacterSet[chr]; thisCharacterPosition = GetCharacterPosition(chargeCursorPosition, chargeValueCharacter); // Raise the superscript Character thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(chargeValueCharacter.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR)); AtomLabelCharacter alcc = new AtomLabelCharacter(thisCharacterPosition, chargeValueCharacter, atomColour, chr, atom.Id); alcc.IsSubScript = true; m_AtomLabelCharacters.Add(alcc); // Move to next Character position chargeCursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(chargeValueCharacter.IncrementX) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0); } } // Insert sign at raised position thisCharacterPosition = GetCharacterPosition(chargeCursorPosition, chargeSignCharacter); thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(hydrogenCharacter.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR)); thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(chargeSignCharacter.Height / 2 * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR)); AtomLabelCharacter alcs = new AtomLabelCharacter(thisCharacterPosition, chargeSignCharacter, atomColour, sign, atom.Id); alcs.IsSubScript = true; m_AtomLabelCharacters.Add(alcs); } #endregion Step 5 - Add Charge if required #region Step 6 Add IsoTope Number if required if (isoValue > 0) { string digits = isoValue.ToString(); xMin = double.MaxValue; yMin = double.MaxValue; xMax = double.MinValue; yMax = double.MinValue; Point isoOrigin = isotopeCursorPosition; // Calculate width of digits foreach (char chr in digits) { TtfCharacter c = m_TtfCharacterSet[chr]; thisCharacterPosition = GetCharacterPosition(isotopeCursorPosition, c); // Raise the superscript Character thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(c.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR)); xMin = Math.Min(xMin, thisCharacterPosition.X); yMin = Math.Min(yMin, thisCharacterPosition.Y); xMax = Math.Max(xMax, thisCharacterPosition.X + OoXmlHelper.ScaleCsTtfToCml(c.Width)); yMax = Math.Max(yMax, thisCharacterPosition.Y + OoXmlHelper.ScaleCsTtfToCml(c.Height)); // Move to next Character position isotopeCursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0); } // Re-position Isotope Cursor width = xMax - xMin; isotopeCursorPosition = new Point(isoOrigin.X - width, isoOrigin.Y); // Insert digits foreach (char chr in digits) { TtfCharacter c = m_TtfCharacterSet[chr]; thisCharacterPosition = GetCharacterPosition(isotopeCursorPosition, c); // Raise the superscript Character thisCharacterPosition.Offset(0, -OoXmlHelper.ScaleCsTtfToCml(c.Height * OoXmlHelper.CS_SUPERSCRIPT_RAISE_FACTOR)); AtomLabelCharacter alcc = new AtomLabelCharacter(thisCharacterPosition, c, atomColour, chr, atom.Id); alcc.IsSubScript = true; m_AtomLabelCharacters.Add(alcc); // Move to next Character position isotopeCursorPosition.Offset(OoXmlHelper.ScaleCsTtfToCml(c.IncrementX) * OoXmlHelper.SUBSCRIPT_SCALE_FACTOR, 0); } } #endregion Step 6 Add IsoTope Number if required } }
public void DrawCharacter(Wpg.WordprocessingGroup wordprocessingGroup1, AtomLabelCharacter alc) { Point characterPosition = new Point(alc.Position.X, alc.Position.Y); characterPosition.Offset(-_canvasExtents.Left, -_canvasExtents.Top); UInt32Value atomLabelId = UInt32Value.FromUInt32((uint)_ooxmlId++); string atomLabelName = "AtomLabel" + atomLabelId; Int64Value width = OoXmlHelper.ScaleCsTtfToEmu(alc.Character.Width); Int64Value height = OoXmlHelper.ScaleCsTtfToEmu(alc.Character.Height); if (alc.IsSubScript) { width = OoXmlHelper.ScaleCsTtfSubScriptToEmu(alc.Character.Width); height = OoXmlHelper.ScaleCsTtfSubScriptToEmu(alc.Character.Height); } Int64Value top = OoXmlHelper.ScaleCmlToEmu(characterPosition.Y); Int64Value left = OoXmlHelper.ScaleCmlToEmu(characterPosition.X); // Set variable true to show bounding box of (every) character if (_options.ShowCharacterBoundingBoxes) { Rect boundingBox = new Rect(new Point(left, top), new Size(width, height)); DrawCharacterBox(wordprocessingGroup1, boundingBox, "00ff00", 10); } Wps.WordprocessingShape wordprocessingShape10 = new Wps.WordprocessingShape(); Wps.NonVisualDrawingProperties nonVisualDrawingProperties10 = new Wps.NonVisualDrawingProperties() { Id = atomLabelId, Name = atomLabelName }; Wps.NonVisualDrawingShapeProperties nonVisualDrawingShapeProperties10 = new Wps.NonVisualDrawingShapeProperties(); Wps.ShapeProperties shapeProperties10 = new Wps.ShapeProperties(); A.Transform2D transform2D10 = new A.Transform2D(); A.Offset offset11 = new A.Offset() { X = left, Y = top }; A.Extents extents11 = new A.Extents() { Cx = width, Cy = height }; transform2D10.Append(offset11); transform2D10.Append(extents11); A.CustomGeometry customGeometry10 = new A.CustomGeometry(); A.AdjustValueList adjustValueList10 = new A.AdjustValueList(); A.Rectangle rectangle10 = new A.Rectangle() { Left = "l", Top = "t", Right = "r", Bottom = "b" }; A.PathList pathList10 = new A.PathList(); A.Path path10 = new A.Path() { Width = width, Height = height }; foreach (TtfContour contour in alc.Character.Contours) { int i = 0; while (i < contour.Points.Count) { TtfPoint thisPoint = contour.Points[i]; TtfPoint nextPoint = null; if (i < contour.Points.Count - 1) { nextPoint = contour.Points[i + 1]; } switch (thisPoint.Type) { case TtfPoint.PointType.Start: A.MoveTo moveTo1 = new A.MoveTo(); if (alc.IsSubScript) { A.Point point1 = new A.Point() { X = OoXmlHelper.ScaleCsTtfSubScriptToEmu(thisPoint.X - alc.Character.OriginX).ToString(), Y = OoXmlHelper.ScaleCsTtfSubScriptToEmu(alc.Character.Height + thisPoint.Y - (alc.Character.Height + alc.Character.OriginY)).ToString() }; moveTo1.Append(point1); path10.Append(moveTo1); } else { A.Point point1 = new A.Point() { X = OoXmlHelper.ScaleCsTtfToEmu(thisPoint.X - alc.Character.OriginX).ToString(), Y = OoXmlHelper.ScaleCsTtfToEmu(alc.Character.Height + thisPoint.Y - (alc.Character.Height + alc.Character.OriginY)).ToString() }; moveTo1.Append(point1); path10.Append(moveTo1); } i++; break; case TtfPoint.PointType.Line: A.LineTo lineTo1 = new A.LineTo(); if (alc.IsSubScript) { A.Point point2 = new A.Point() { X = OoXmlHelper.ScaleCsTtfSubScriptToEmu(thisPoint.X - alc.Character.OriginX).ToString(), Y = OoXmlHelper.ScaleCsTtfSubScriptToEmu(alc.Character.Height + thisPoint.Y - (alc.Character.Height + alc.Character.OriginY)).ToString() }; lineTo1.Append(point2); path10.Append(lineTo1); } else { A.Point point2 = new A.Point() { X = OoXmlHelper.ScaleCsTtfToEmu(thisPoint.X - alc.Character.OriginX).ToString(), Y = OoXmlHelper.ScaleCsTtfToEmu(alc.Character.Height + thisPoint.Y - (alc.Character.Height + alc.Character.OriginY)).ToString() }; lineTo1.Append(point2); path10.Append(lineTo1); } i++; break; case TtfPoint.PointType.CurveOff: A.QuadraticBezierCurveTo quadraticBezierCurveTo13 = new A.QuadraticBezierCurveTo(); if (alc.IsSubScript) { A.Point point3 = new A.Point() { X = OoXmlHelper.ScaleCsTtfSubScriptToEmu(thisPoint.X - alc.Character.OriginX).ToString(), Y = OoXmlHelper.ScaleCsTtfSubScriptToEmu(alc.Character.Height + thisPoint.Y - (alc.Character.Height + alc.Character.OriginY)).ToString() }; A.Point point4 = new A.Point() { X = OoXmlHelper.ScaleCsTtfSubScriptToEmu(nextPoint.X - alc.Character.OriginX).ToString(), Y = OoXmlHelper.ScaleCsTtfSubScriptToEmu(alc.Character.Height + nextPoint.Y - (alc.Character.Height + alc.Character.OriginY)).ToString() }; quadraticBezierCurveTo13.Append(point3); quadraticBezierCurveTo13.Append(point4); path10.Append(quadraticBezierCurveTo13); } else { A.Point point3 = new A.Point() { X = OoXmlHelper.ScaleCsTtfToEmu(thisPoint.X - alc.Character.OriginX).ToString(), Y = OoXmlHelper.ScaleCsTtfToEmu(alc.Character.Height + thisPoint.Y - (alc.Character.Height + alc.Character.OriginY)).ToString() }; A.Point point4 = new A.Point() { X = OoXmlHelper.ScaleCsTtfToEmu(nextPoint.X - alc.Character.OriginX).ToString(), Y = OoXmlHelper.ScaleCsTtfToEmu(alc.Character.Height + nextPoint.Y - (alc.Character.Height + alc.Character.OriginY)).ToString() }; quadraticBezierCurveTo13.Append(point3); quadraticBezierCurveTo13.Append(point4); path10.Append(quadraticBezierCurveTo13); } i++; i++; break; case TtfPoint.PointType.CurveOn: // Should never get here ! i++; break; } } A.CloseShapePath closeShapePath1 = new A.CloseShapePath(); path10.Append(closeShapePath1); } pathList10.Append(path10); customGeometry10.Append(adjustValueList10); customGeometry10.Append(rectangle10); customGeometry10.Append(pathList10); A.SolidFill solidFill10 = new A.SolidFill(); // Set Colour A.RgbColorModelHex rgbColorModelHex10 = new A.RgbColorModelHex() { Val = alc.Colour }; solidFill10.Append(rgbColorModelHex10); shapeProperties10.Append(transform2D10); shapeProperties10.Append(customGeometry10); shapeProperties10.Append(solidFill10); Wps.ShapeStyle shapeStyle10 = new Wps.ShapeStyle(); A.LineReference lineReference10 = new A.LineReference() { Index = (UInt32Value)0U }; A.FillReference fillReference10 = new A.FillReference() { Index = (UInt32Value)0U }; A.EffectReference effectReference10 = new A.EffectReference() { Index = (UInt32Value)0U }; A.FontReference fontReference10 = new A.FontReference() { Index = A.FontCollectionIndexValues.Minor }; shapeStyle10.Append(lineReference10); shapeStyle10.Append(fillReference10); shapeStyle10.Append(effectReference10); shapeStyle10.Append(fontReference10); Wps.TextBodyProperties textBodyProperties10 = new Wps.TextBodyProperties(); wordprocessingShape10.Append(nonVisualDrawingProperties10); wordprocessingShape10.Append(nonVisualDrawingShapeProperties10); wordprocessingShape10.Append(shapeProperties10); wordprocessingShape10.Append(shapeStyle10); wordprocessingShape10.Append(textBodyProperties10); wordprocessingGroup1.Append(wordprocessingShape10); }