private ParagraphIterator SeekLastLeaf() { DocumentObject obj = Current; if (!(obj is ParagraphElements)) { return(this); } List <int> indices = new List <int>(_positionIndices); while (obj is ParagraphElements) { ParagraphElements parEls = (ParagraphElements)obj; if (((ParagraphElements)obj).Count == 0) { return(new ParagraphIterator(_rootNode, obj, indices)); } int idx = ((ParagraphElements)obj).Count - 1; indices.Add(idx); obj = GetNodeObject(parEls[idx]); } return(new ParagraphIterator(_rootNode, obj, indices)); }
private static void ProcessParagraphElements(ParagraphElements pe, XmlWriter xmlw) { xmlw.WriteStartElement("ParagraphElements"); xmlw.WriteAttributeString("Count", pe.Count.ToStringSafe()); foreach (ParagraphElement p in pe) { ProcessParagraphElement(p, xmlw); } xmlw.WriteEndElement(); }
private static void ProcessParagraphElements(ParagraphElements pe, Utf8JsonWriter jsonw) { jsonw.WritePropertyName("ParagraphElements"); jsonw.WriteStartObject(); jsonw.WriteString("Count", pe.Count.ToStringSafe()); jsonw.WritePropertyName("Items"); jsonw.WriteStartArray(); foreach (ParagraphElement p in pe) { ProcessParagraphElement(p, jsonw); } jsonw.WriteEndArray(); jsonw.WriteEndObject(); }
/// <summary> /// Returns the previous iterator to a leaf in the document object tree pointing. /// </summary> /// <returns>The previous leaf, null if none exists.</returns> internal ParagraphIterator GetPreviousLeaf() { //Move up to appropriate parent element ParagraphIterator parIterator = GetParentIterator(); if (parIterator == null) { return(null); } int elementIndex = LastIndex; ParagraphElements parEls = (ParagraphElements)parIterator._current; while (elementIndex == 0) { elementIndex = parIterator.LastIndex; parIterator = parIterator.GetParentIterator(); if (parIterator == null) { break; } parEls = (ParagraphElements)parIterator._current; } if (parIterator == null) { return(null); } int newIndex = elementIndex - 1; if (newIndex < 0) { return(null); } List <int> indices = new List <int>(parIterator._positionIndices);//(Array_List)parIterator.positionIndices.Clone(); indices.Add(newIndex); DocumentObject obj = GetNodeObject(parEls[newIndex]); ParagraphIterator iterator = new ParagraphIterator(_rootNode, obj, indices); return(iterator.SeekLastLeaf()); }
/// <summary> /// Returns the next iterator in the tree pointing to a leaf. /// </summary> /// <remarks>This function is intended to receive the renderable objects of a paragraph. /// Thus, empty ParagraphElement objects (which are collections) don't count as leafs.</remarks> internal ParagraphIterator GetNextLeaf() { //Move up to appropriate parent element ParagraphIterator parIterator = GetParentIterator(); if (parIterator == null) { return(null); } int elementIndex = this.LastIndex; ParagraphElements parEls = (ParagraphElements)parIterator.current; while (elementIndex == parEls.Count - 1) { elementIndex = parIterator.LastIndex; parIterator = parIterator.GetParentIterator(); if (parIterator == null) { break; } parEls = (ParagraphElements)parIterator.current; } if (parIterator == null) { return(null); } int newIndex = elementIndex + 1; if (newIndex >= parEls.Count) { return(null); } ArrayList indices = (ArrayList)parIterator.positionIndices.Clone(); indices.Add(newIndex); DocumentObject obj = GetNodeObject(parEls[newIndex]); ParagraphIterator iterator = new ParagraphIterator(this.rootNode, obj, indices); return(iterator.SeekFirstLeaf()); }
/// <summary> /// Return all text elements in the paragraph - including the text elements /// inside the FormattedText elements. /// </summary> /// <param name="paragraph">The paragraph.</param> internal static IEnumerable <string> GetTextElements(this ParagraphElements paragraph) { if (paragraph == null) { throw new ArgumentNullException(nameof(paragraph)); } foreach (DocumentObject obj in paragraph) { if (obj is Text text) { yield return(text.Content); } else if (obj is Character character) { if (character.SymbolName == SymbolName.LineBreak) { yield return(Environment.NewLine); } else { // This probably doesn't work. yield return(new string(character.Char, character.Count)); } } else if (obj is FormattedText formattedText) { foreach (string subtext in formattedText.Elements.GetTextElements()) { yield return(subtext); } } else if (obj is Hyperlink link) { foreach (string subtext in link.Elements.GetTextElements()) { yield return(subtext); } } } }
/// <summary> /// Gets the leftmost leaf within the hierarchy. /// </summary> /// <returns>The searched leaf.</returns> ParagraphIterator SeekFirstLeaf() { DocumentObject obj = this.Current; if (!(obj is ParagraphElements)) { return(this); } ArrayList indices = (ArrayList)this.positionIndices.Clone(); while (obj is ParagraphElements) { ParagraphElements parEls = (ParagraphElements)obj; if (parEls.Count == 0) { return(new ParagraphIterator(this.rootNode, obj, indices)); } indices.Add(0); obj = GetNodeObject(parEls[0]); } return(new ParagraphIterator(this.rootNode, obj, indices)); }
/// <summary> /// Gets the leftmost leaf within the hierarchy. /// </summary> /// <returns>The searched leaf.</returns> ParagraphIterator SeekFirstLeaf() { DocumentObject obj = Current; if (!(obj is ParagraphElements)) { return(this); } List <int> indices = new List <int>(_positionIndices); while (obj is ParagraphElements) { ParagraphElements parEls = (ParagraphElements)obj; if (parEls.Count == 0) { return(new ParagraphIterator(_rootNode, obj, indices)); } indices.Add(0); obj = GetNodeObject(parEls[0]); } return(new ParagraphIterator(_rootNode, obj, indices)); }
/// <summary> /// Parses the keyword «\symbol» resp. «\(». /// </summary> private void ParseSymbol(ParagraphElements elements) { AssertSymbol(Symbol.Symbol); ReadCode(); // read '(' AssertSymbol(Symbol.ParenLeft); const char ch = (char)0; SymbolName symtype = 0; int count = 1; ReadCode(); // read name if (TokenType == TokenType.Identifier) { try { if (Enum.IsDefined(typeof(SymbolName), Token)) { AssertCondition(IsSymbolType(Token), DomMsgID.InvalidSymbolType, Token); symtype = (SymbolName)Enum.Parse(typeof(SymbolName), Token, true); } } catch (Exception ex) { ThrowParserException(ex, DomMsgID.InvalidEnum, Token); } } else { ThrowParserException(DomMsgID.UnexpectedSymbol, Token); } ReadCode(); // read integer or identifier if (Symbol == Symbol.Comma) { ReadCode(); // read integer if (TokenType == TokenType.IntegerLiteral) count = _scanner.GetTokenValueAsInt(); ReadCode(); } AssertSymbol(Symbol.ParenRight); if (symtype != 0) elements.AddCharacter(symtype, count); else elements.AddCharacter(ch, count); }
/// <summary> /// Parses the keyword «\chr». /// </summary> private void ParseChr(ParagraphElements elements) { AssertSymbol(Symbol.Chr); ReadCode(); // read '(' AssertSymbol(Symbol.ParenLeft); char ch = (char)0; SymbolName symtype = 0; int count = 1; ReadCode(); // read integer if (TokenType == TokenType.IntegerLiteral) { int val = _scanner.GetTokenValueAsInt(); if (val >= 1 && val < 256) ch = (char)val; else ThrowParserException(DomMsgID.OutOfRange, "1 - 255"); } else { ThrowParserException(DomMsgID.UnexpectedSymbol, Token); } ReadCode(); // read integer or identifier if (Symbol == Symbol.Comma) { ReadCode(); // read integer if (TokenType == TokenType.IntegerLiteral) count = _scanner.GetTokenValueAsInt(); ReadCode(); } AssertSymbol(Symbol.ParenRight); if (symtype != 0) elements.AddCharacter(symtype, count); else elements.AddCharacter(ch, count); }
/// <summary> /// Removes the last blank from the text. Used before a tab, a linebreak or a space will be /// added to the text. /// </summary> private void RemoveTrailingBlank(ParagraphElements elements) { DocumentObject dom = elements.LastObject; Text text = dom as Text; if (text != null) { if (text.Content.EndsWith(" ")) text.Content = text.Content.Remove(text.Content.Length - 1, 1); } }
/// <summary> /// Parses the inner text of a paragraph. Parsing ends if '}' is reached or an empty /// line occurs on nesting level 0. /// </summary> private void ParseFormattedText(ParagraphElements elements, int nestingLevel) { MoveToParagraphContent(); bool loop = true; bool rootLevel = nestingLevel == 0; ReadText(rootLevel); while (loop) { switch (Symbol) { case Symbol.Eof: ThrowParserException(DomMsgID.UnexpectedEndOfFile); break; case Symbol.EmptyLine: elements.AddCharacter(SymbolName.ParaBreak); ReadText(rootLevel); break; case Symbol.BraceRight: loop = false; break; case Symbol.Comment: // Ignore comments. ReadText(rootLevel); break; case Symbol.Text: elements.AddText(Token); ReadText(rootLevel); break; case Symbol.Tab: RemoveTrailingBlank(elements); elements.AddTab(); _scanner.MoveToNonWhiteSpaceOrEol(); ReadText(rootLevel); break; case Symbol.LineBreak: RemoveTrailingBlank(elements); elements.AddLineBreak(); _scanner.MoveToNonWhiteSpaceOrEol(); ReadText(rootLevel); break; case Symbol.Bold: ParseBoldItalicEtc(elements.AddFormattedText(TextFormat.Bold), nestingLevel + 1); ReadText(rootLevel); break; case Symbol.Italic: ParseBoldItalicEtc(elements.AddFormattedText(TextFormat.Italic), nestingLevel + 1); ReadText(rootLevel); break; case Symbol.Underline: ParseBoldItalicEtc(elements.AddFormattedText(TextFormat.Underline), nestingLevel + 1); ReadText(rootLevel); break; case Symbol.Font: ParseFont(elements.AddFormattedText(), nestingLevel + 1); ReadText(rootLevel); break; case Symbol.FontSize: ParseFontSize(elements.AddFormattedText(), nestingLevel + 1); ReadText(rootLevel); break; case Symbol.FontColor: ParseFontColor(elements.AddFormattedText(), nestingLevel + 1); ReadText(rootLevel); break; case Symbol.Image: ParseImage(elements.AddImage(""), true); ReadText(rootLevel); break; case Symbol.Field: ParseField(elements, nestingLevel + 1); ReadText(rootLevel); break; case Symbol.Footnote: ParseFootnote(elements, nestingLevel + 1); ReadText(rootLevel); break; case Symbol.Hyperlink: ParseHyperlink(elements, nestingLevel + 1); ReadText(rootLevel); break; case Symbol.Space: RemoveTrailingBlank(elements); ParseSpace(elements, nestingLevel + 1); _scanner.MoveToNonWhiteSpaceOrEol(); ReadText(rootLevel); break; case Symbol.Symbol: ParseSymbol(elements); ReadText(rootLevel); break; case Symbol.Chr: ParseChr(elements); ReadText(rootLevel); break; default: ThrowParserException(DomMsgID.UnexpectedSymbol, Token); break; } } }
/// <summary> /// Removes the last blank from the text. Used before a tab, a linebreak or a space will be /// added to the text. /// </summary> private void RemoveTrailingBlank(ParagraphElements elements) { DocumentObject dom = elements.LastObject; if (dom is Text) { Text text = (Text)dom; if (text.Content.EndsWith(" ")) text.Content = text.Content.Remove(text.Content.Length - 1, 1); } }
/// <summary> /// Initializes a paragraph iterator pointing on the given paragraph elements object. /// Paragraph iterators received from this paragraph iterator relate to this root node. /// </summary> /// <param name="rootNode">The root node for the paragraph iterator.</param> internal ParagraphIterator(ParagraphElements rootNode) { this.rootNode = rootNode; this.current = rootNode; this.positionIndices = new ArrayList(); }
/// <summary> /// Initializes a paragraph iterator given the root node, its position in the object tree and the current object /// </summary> /// <param name="rootNode">The node the position indices relate to.</param> /// <param name="current">The element the iterator shall point to.</param> /// <param name="indices">The position of the paragraph iterator in terms of element indices.</param> private ParagraphIterator(ParagraphElements rootNode, DocumentObject current, ArrayList indices) { this.rootNode = rootNode; this.positionIndices = indices; this.current = current; }
/// <summary> /// Parses the keyword «\hyperlink». /// </summary> private void ParseHyperlink(ParagraphElements elements, int nestingLevel) { AssertSymbol(Symbol.Hyperlink); ReadCode(); Hyperlink hyperlink = elements.AddHyperlink(""); //NYI: Without name and type the hyperlink is senseless, so attributes need to be checked if (Symbol == Symbol.BracketLeft) ParseAttributes(hyperlink); AssertSymbol(Symbol.BraceLeft); ParseFormattedText(hyperlink.Elements, nestingLevel); AssertSymbol(Symbol.BraceRight); }
/// <summary> /// Parses the keyword «\field». /// </summary> private void ParseField(ParagraphElements elements, int nestingLevel) { AssertSymbol(Symbol.Field); ReadCode(); // read '(' AssertSymbol(Symbol.ParenLeft); ReadCode(); // read identifier AssertSymbol(Symbol.Identifier); string fieldType = Token.ToLower(); ReadCode(); // read ')' AssertSymbol(Symbol.ParenRight); DocumentObject field = null; switch (fieldType) { case "date": field = elements.AddDateField(); break; case "page": field = elements.AddPageField(); break; case "numpages": field = elements.AddNumPagesField(); break; case "info": field = elements.AddInfoField(0); break; case "sectionpages": field = elements.AddSectionPagesField(); break; case "section": field = elements.AddSectionField(); break; case "bookmark": field = elements.AddBookmark(""); break; case "pageref": field = elements.AddPageRefField(""); break; } AssertCondition(field != null, DomMsgID.InvalidFieldType, Token); if (_scanner.PeekSymbol() == Symbol.BracketLeft) { ReadCode(); // read '[' ParseAttributes(field, false); } }
/// <summary> /// Parses the keyword «\footnote». /// </summary> private void ParseFootnote(ParagraphElements elements, int nestingLevel) { AssertSymbol(Symbol.Footnote); ReadCode(); Footnote footnote = elements.AddFootnote(); if (Symbol == Symbol.BracketLeft) ParseAttributes(footnote); AssertSymbol(Symbol.BraceLeft); // The keyword «\paragraph» is typically ommitted. if (IsParagraphContent()) { Paragraph paragraph = footnote.Elements.AddParagraph(); ParseParagraphContent(footnote.Elements, paragraph); } else { ReadCode(); // read beyond '{' ParseDocumentElements(footnote.Elements, Symbol.Footnote); } AssertSymbol(Symbol.BraceRight); }
/// <summary> /// Initializes a paragraph iterator pointing on the given paragraph elements object. /// Paragraph iterators received from this paragraph iterator relate to this root node. /// </summary> /// <param name="rootNode">The root node for the paragraph iterator.</param> internal ParagraphIterator(ParagraphElements rootNode) { _rootNode = rootNode; _current = rootNode; _positionIndices = new List <int>(); }
/// <summary> /// Parses the keyword «\space». /// </summary> private void ParseSpace(ParagraphElements elements, int nestingLevel) { // Samples // \space // \space(5) // \space(em) // \space(em,5) AssertSymbol(Symbol.Space); Character space = elements.AddSpace(1); // «\space» can stand alone if (_scanner.PeekSymbol() == Symbol.ParenLeft) { ReadCode(); // read '(' AssertSymbol(Symbol.ParenLeft); ReadCode(); // read beyond '(' if (Symbol == Symbol.Identifier) { string type = Token; if (!IsSpaceType(type)) ThrowParserException(DomMsgID.InvalidEnum, type); space.SymbolName = (SymbolName)Enum.Parse(typeof(SymbolName), type, true); ReadCode(); // read ',' or ')' if (Symbol == Symbol.Comma) { ReadCode(); // read integer AssertSymbol(Symbol.IntegerLiteral); space.Count = _scanner.GetTokenValueAsInt(); ReadCode(); // read ')' } } else if (Symbol == Symbol.IntegerLiteral) { space.Count = _scanner.GetTokenValueAsInt(); ReadCode(); } AssertSymbol(Symbol.ParenRight); } }
/// <summary> /// Initializes a paragraph iterator given the root node, its position in the object tree and the current object /// </summary> /// <param name="rootNode">The node the position indices relate to.</param> /// <param name="current">The element the iterator shall point to.</param> /// <param name="indices">The position of the paragraph iterator in terms of element indices.</param> private ParagraphIterator(ParagraphElements rootNode, DocumentObject current, List <int> indices) { _rootNode = rootNode; _positionIndices = indices; _current = current; }