2722 lines
92 KiB
C#
2722 lines
92 KiB
C#
|
#region MigraDoc - Creating Documents on the Fly
|
|||
|
//
|
|||
|
// Authors:
|
|||
|
// Stefan Lange
|
|||
|
// Klaus Potzesny
|
|||
|
// David Stephensen
|
|||
|
//
|
|||
|
// Copyright (c) 2001-2017 empira Software GmbH, Cologne Area (Germany)
|
|||
|
//
|
|||
|
// http://www.pdfsharp.com
|
|||
|
// http://www.migradoc.com
|
|||
|
// http://sourceforge.net/projects/pdfsharp
|
|||
|
//
|
|||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|||
|
// copy of this software and associated documentation files (the "Software"),
|
|||
|
// to deal in the Software without restriction, including without limitation
|
|||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|||
|
// Software is furnished to do so, subject to the following conditions:
|
|||
|
//
|
|||
|
// The above copyright notice and this permission notice shall be included
|
|||
|
// in all copies or substantial portions of the Software.
|
|||
|
//
|
|||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|||
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|||
|
// DEALINGS IN THE SOFTWARE.
|
|||
|
#endregion
|
|||
|
|
|||
|
using System;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.Globalization;
|
|||
|
using MigraDoc.DocumentObjectModel.publics;
|
|||
|
using MigraDoc.DocumentObjectModel.Tables;
|
|||
|
using MigraDoc.DocumentObjectModel.Shapes;
|
|||
|
using MigraDoc.DocumentObjectModel.Shapes.Charts;
|
|||
|
|
|||
|
namespace MigraDoc.DocumentObjectModel.IO
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// A simple hand-coded parser for MigraDoc DDL.
|
|||
|
/// </summary>
|
|||
|
public class DdlParser
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// Initializes a new instance of the DdlParser class.
|
|||
|
/// </summary>
|
|||
|
public DdlParser(string ddl, DdlReaderErrors errors)
|
|||
|
: this(String.Empty, ddl, errors)
|
|||
|
{ }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Initializes a new instance of the DdlParser class.
|
|||
|
/// </summary>
|
|||
|
public DdlParser(string fileName, string ddl, DdlReaderErrors errors)
|
|||
|
{
|
|||
|
_errors = errors ?? new DdlReaderErrors();
|
|||
|
_scanner = new DdlScanner(fileName, ddl, errors);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\document<6E>.
|
|||
|
/// </summary>
|
|||
|
public Document ParseDocument(Document document)
|
|||
|
{
|
|||
|
if (document == null)
|
|||
|
document = new Document();
|
|||
|
|
|||
|
MoveToCode();
|
|||
|
AssertSymbol(Symbol.Document);
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(document);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
|
|||
|
// Styles come first
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.Styles)
|
|||
|
ParseStyles(document.Styles);
|
|||
|
|
|||
|
// A document with no sections is valid and has zero pages.
|
|||
|
while (Symbol == Symbol.Section)
|
|||
|
ParseSection(document.Sections);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode();
|
|||
|
AssertCondition(Symbol == Symbol.Eof, DomMsgID.EndOfFileExpected);
|
|||
|
|
|||
|
return document;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses one of the keywords <20>\document<6E>, <20>\styles<65>, <20>\section<6F>, <20>\table<6C>, <20>\textframe<6D>, <20>\chart<72>
|
|||
|
/// and <20>\paragraph<70> and returns the corresponding DocumentObject or DocumentObjectCollection.
|
|||
|
/// </summary>
|
|||
|
public DocumentObject ParseDocumentObject()
|
|||
|
{
|
|||
|
DocumentObject obj = null;
|
|||
|
|
|||
|
MoveToCode();
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.Document:
|
|||
|
obj = ParseDocument(null);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Styles:
|
|||
|
obj = ParseStyles(new Styles());
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Section:
|
|||
|
obj = ParseSection(new Sections());
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Table:
|
|||
|
obj = new Table();
|
|||
|
ParseTable(null, (Table)obj);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.TextFrame:
|
|||
|
DocumentElements elems = new DocumentElements();
|
|||
|
ParseTextFrame(elems);
|
|||
|
obj = elems[0];
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Chart:
|
|||
|
throw new NotImplementedException();
|
|||
|
|
|||
|
case Symbol.Paragraph:
|
|||
|
obj = new DocumentElements();
|
|||
|
ParseParagraph((DocumentElements)obj);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedSymbol);
|
|||
|
break;
|
|||
|
}
|
|||
|
ReadCode();
|
|||
|
AssertCondition(Symbol == Symbol.Eof, DomMsgID.EndOfFileExpected);
|
|||
|
|
|||
|
return obj;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\styles<65>.
|
|||
|
/// </summary>
|
|||
|
private Styles ParseStyles(Styles styles)
|
|||
|
{
|
|||
|
MoveToCode();
|
|||
|
AssertSymbol(Symbol.Styles);
|
|||
|
|
|||
|
ReadCode(); // read '{'
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
|
|||
|
ReadCode(); // read first style name
|
|||
|
// An empty \styles block is valid.
|
|||
|
while (Symbol == Symbol.Identifier || Symbol == Symbol.StringLiteral)
|
|||
|
ParseStyleDefinition(styles);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
|
|||
|
return styles;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses a style definition block within the keyword <20>\styles<65>.
|
|||
|
/// </summary>
|
|||
|
private Style ParseStyleDefinition(Styles styles)
|
|||
|
{
|
|||
|
// StyleName [: BaseStyleName]
|
|||
|
// {
|
|||
|
// ...
|
|||
|
// }
|
|||
|
Style style = null;
|
|||
|
try
|
|||
|
{
|
|||
|
string styleName = _scanner.Token;
|
|||
|
string baseStyleName = null;
|
|||
|
|
|||
|
if (Symbol != Symbol.Identifier && Symbol != Symbol.StringLiteral)
|
|||
|
ThrowParserException(DomMsgID.StyleNameExpected, styleName);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
|
|||
|
if (Symbol == Symbol.Colon)
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
if (Symbol != Symbol.Identifier && Symbol != Symbol.StringLiteral)
|
|||
|
ThrowParserException(DomMsgID.StyleNameExpected, styleName);
|
|||
|
|
|||
|
// If baseStyle is not valid, choose InvalidStyleName by default.
|
|||
|
baseStyleName = _scanner.Token;
|
|||
|
if (styles.GetIndex(baseStyleName) == -1)
|
|||
|
{
|
|||
|
ReportParserInfo(DdlErrorLevel.Warning, DomMsgID.UseOfUndefinedBaseStyle, baseStyleName);
|
|||
|
baseStyleName = StyleNames.InvalidStyleName;
|
|||
|
}
|
|||
|
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
// Get or create style.
|
|||
|
style = styles[styleName];
|
|||
|
if (style != null)
|
|||
|
{
|
|||
|
// Reset base style.
|
|||
|
if (baseStyleName != null)
|
|||
|
style.BaseStyle = baseStyleName;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Style does not exist and no base style is given, choose InvalidStyleName by default.
|
|||
|
if (String.IsNullOrEmpty(baseStyleName))
|
|||
|
{
|
|||
|
baseStyleName = StyleNames.InvalidStyleName;
|
|||
|
ReportParserInfo(DdlErrorLevel.Warning, DomMsgID.UseOfUndefinedStyle, styleName);
|
|||
|
}
|
|||
|
|
|||
|
style = styles.AddStyle(styleName, baseStyleName);
|
|||
|
}
|
|||
|
|
|||
|
// Parse definition (if any).
|
|||
|
|
|||
|
if (Symbol == Symbol.BraceLeft)
|
|||
|
{
|
|||
|
ParseAttributeBlock(style);
|
|||
|
}
|
|||
|
}
|
|||
|
catch (DdlParserException ex)
|
|||
|
{
|
|||
|
ReportParserException(ex);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
return style;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Determines if the current symbol is a header or footer.
|
|||
|
/// </summary>
|
|||
|
private bool IsHeaderFooter()
|
|||
|
{
|
|||
|
Symbol sym = Symbol;
|
|||
|
return (sym == Symbol.Header || sym == Symbol.Footer ||
|
|||
|
sym == Symbol.PrimaryHeader || sym == Symbol.PrimaryFooter ||
|
|||
|
sym == Symbol.EvenPageHeader || sym == Symbol.EvenPageFooter ||
|
|||
|
sym == Symbol.FirstPageHeader || sym == Symbol.FirstPageFooter);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\section<6F>.
|
|||
|
/// </summary>
|
|||
|
private Section ParseSection(Sections sections)
|
|||
|
{
|
|||
|
Debug.Assert(sections != null);
|
|||
|
|
|||
|
MoveToCode();
|
|||
|
AssertSymbol(Symbol.Section);
|
|||
|
|
|||
|
Section section = null;
|
|||
|
try
|
|||
|
{
|
|||
|
section = sections.AddSection();
|
|||
|
|
|||
|
ReadCode(); // read '[' or '{'
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(section);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
|
|||
|
// Consider the case that the keyword <20>\paragraph<70> can be omitted.
|
|||
|
if (IsParagraphContent())
|
|||
|
{
|
|||
|
Paragraph paragraph = section.Elements.AddParagraph();
|
|||
|
ParseParagraphContent(section.Elements, paragraph);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ReadCode(); // read beyond '{'
|
|||
|
|
|||
|
// 1st parse headers and footers
|
|||
|
while (IsHeaderFooter())
|
|||
|
ParseHeaderFooter(section);
|
|||
|
|
|||
|
// 2nd parse all other stuff
|
|||
|
ParseDocumentElements(section.Elements, Symbol.Section);
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
catch (DdlParserException ex)
|
|||
|
{
|
|||
|
ReportParserException(ex);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
return section;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keywords <20>\header<65>.
|
|||
|
/// </summary>
|
|||
|
private void ParseHeaderFooter(Section section)
|
|||
|
{
|
|||
|
if (section == null)
|
|||
|
throw new ArgumentNullException("section");
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
Symbol hdrFtrSym = Symbol;
|
|||
|
bool isHeader = hdrFtrSym == Symbol.Header ||
|
|||
|
hdrFtrSym == Symbol.PrimaryHeader ||
|
|||
|
hdrFtrSym == Symbol.FirstPageHeader ||
|
|||
|
hdrFtrSym == Symbol.EvenPageHeader;
|
|||
|
|
|||
|
// Recall that the styles "Header" resp. "Footer" are used as default if
|
|||
|
// no other style was given. But this belongs to the rendering process,
|
|||
|
// not to the DDL parser. Therefore no code here belongs to that.
|
|||
|
HeaderFooter headerFooter = new HeaderFooter();
|
|||
|
ReadCode(); // read '[' or '{'
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(headerFooter);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
if (IsParagraphContent())
|
|||
|
{
|
|||
|
Paragraph paragraph = headerFooter.Elements.AddParagraph();
|
|||
|
ParseParagraphContent(headerFooter.Elements, paragraph);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ReadCode(); // parse '{'
|
|||
|
ParseDocumentElements(headerFooter.Elements, Symbol.HeaderOrFooter);
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode(); // parse beyond '{'
|
|||
|
|
|||
|
HeadersFooters headersFooters = isHeader ? section.Headers : section.Footers;
|
|||
|
if (hdrFtrSym == Symbol.Header || hdrFtrSym == Symbol.Footer)
|
|||
|
{
|
|||
|
headersFooters.Primary = headerFooter.Clone();
|
|||
|
headersFooters.EvenPage = headerFooter.Clone();
|
|||
|
headersFooters.FirstPage = headerFooter.Clone();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
switch (hdrFtrSym)
|
|||
|
{
|
|||
|
case Symbol.PrimaryHeader:
|
|||
|
case Symbol.PrimaryFooter:
|
|||
|
headersFooters.Primary = headerFooter;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.EvenPageHeader:
|
|||
|
case Symbol.EvenPageFooter:
|
|||
|
headersFooters.EvenPage = headerFooter;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.FirstPageHeader:
|
|||
|
case Symbol.FirstPageFooter:
|
|||
|
headersFooters.FirstPage = headerFooter;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
catch (DdlParserException ex)
|
|||
|
{
|
|||
|
ReportParserException(ex);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Determines whether the next text is paragraph content or document element.
|
|||
|
/// </summary>
|
|||
|
private bool IsParagraphContent()
|
|||
|
{
|
|||
|
if (MoveToParagraphContent())
|
|||
|
{
|
|||
|
if (_scanner.Char == Chars.BackSlash)
|
|||
|
{
|
|||
|
Symbol symbol = _scanner.PeekKeyword();
|
|||
|
switch (symbol)
|
|||
|
{
|
|||
|
case Symbol.Bold:
|
|||
|
case Symbol.Italic:
|
|||
|
case Symbol.Underline:
|
|||
|
case Symbol.Field:
|
|||
|
case Symbol.Font:
|
|||
|
case Symbol.FontColor:
|
|||
|
case Symbol.FontSize:
|
|||
|
case Symbol.Footnote:
|
|||
|
case Symbol.Hyperlink:
|
|||
|
case Symbol.Symbol:
|
|||
|
case Symbol.Chr:
|
|||
|
case Symbol.Tab:
|
|||
|
case Symbol.LineBreak:
|
|||
|
case Symbol.Space:
|
|||
|
case Symbol.SoftHyphen:
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the document elements of a <20>\paragraph<70>, <20>\cell<6C> or comparable.
|
|||
|
/// </summary>
|
|||
|
private DocumentElements ParseDocumentElements(DocumentElements elements, Symbol context)
|
|||
|
{
|
|||
|
//
|
|||
|
// This is clear:
|
|||
|
// \section { Hallo World! }
|
|||
|
// All section content will be treated as paragraph content.
|
|||
|
//
|
|||
|
// but this is ambiguous:
|
|||
|
// \section { \image(...) }
|
|||
|
// It could be an image inside a paragraph or at the section level.
|
|||
|
// In this case it will be treated as an image on section level.
|
|||
|
//
|
|||
|
// If this is not your intention it must be like this:
|
|||
|
// \section { \paragraph { \image(...) } }
|
|||
|
//
|
|||
|
|
|||
|
while (TokenType == TokenType.KeyWord)
|
|||
|
{
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.Paragraph:
|
|||
|
ParseParagraph(elements);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.PageBreak:
|
|||
|
ParsePageBreak(elements);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Table:
|
|||
|
ParseTable(elements, null);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.TextFrame:
|
|||
|
ParseTextFrame(elements);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Image:
|
|||
|
ParseImage(elements.AddImage(""), false);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Chart:
|
|||
|
ParseChart(elements);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Barcode:
|
|||
|
ParseBarcode(elements);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedSymbol, _scanner.Token);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
return elements;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\paragraph<70>.
|
|||
|
/// </summary>
|
|||
|
private void ParseParagraph(DocumentElements elements)
|
|||
|
{
|
|||
|
MoveToCode();
|
|||
|
AssertSymbol(Symbol.Paragraph);
|
|||
|
|
|||
|
Paragraph paragraph = elements.AddParagraph();
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode(); // read '[' or '{'
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(paragraph);
|
|||
|
|
|||
|
// Empty paragraphs without braces are valid.
|
|||
|
if (Symbol == Symbol.BraceLeft)
|
|||
|
{
|
|||
|
ParseParagraphContent(elements, paragraph);
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
}
|
|||
|
catch (DdlParserException ex)
|
|||
|
{
|
|||
|
ReportParserException(ex);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the inner text of a paragraph, i.e. stops on BraceRight and treats empty
|
|||
|
/// line as paragraph separator.
|
|||
|
/// </summary>
|
|||
|
private void ParseParagraphContent(DocumentElements elements, Paragraph paragraph)
|
|||
|
{
|
|||
|
Paragraph para = paragraph ?? elements.AddParagraph();
|
|||
|
|
|||
|
while (para != null)
|
|||
|
{
|
|||
|
ParseFormattedText(para.Elements, 0);
|
|||
|
if (Symbol != Symbol.BraceRight && Symbol != Symbol.Eof)
|
|||
|
{
|
|||
|
para = elements.AddParagraph();
|
|||
|
}
|
|||
|
else
|
|||
|
para = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <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>
|
|||
|
/// Parses the keywords <20>\bold<6C>, <20>\italic<69>, and <20>\underline<6E>.
|
|||
|
/// </summary>
|
|||
|
private void ParseBoldItalicEtc(FormattedText formattedText, int nestingLevel)
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
ParseFormattedText(formattedText.Elements, nestingLevel);
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\font<6E>.
|
|||
|
/// </summary>
|
|||
|
private void ParseFont(FormattedText formattedText, int nestingLevel)
|
|||
|
{
|
|||
|
AssertSymbol(Symbol.Font);
|
|||
|
ReadCode();
|
|||
|
|
|||
|
if (Symbol == Symbol.ParenLeft)
|
|||
|
{
|
|||
|
formattedText.Style = ParseElementName();
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(formattedText);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
ParseFormattedText(formattedText.Elements, nestingLevel);
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses code like <20>("name")<29>.
|
|||
|
/// </summary>
|
|||
|
private string ParseElementName()
|
|||
|
{
|
|||
|
AssertSymbol(Symbol.ParenLeft);
|
|||
|
ReadCode();
|
|||
|
if (Symbol != Symbol.StringLiteral)
|
|||
|
ThrowParserException(DomMsgID.StringExpected, Token);
|
|||
|
|
|||
|
string name = Token;
|
|||
|
ReadCode();
|
|||
|
AssertSymbol(Symbol.ParenRight);
|
|||
|
|
|||
|
return name;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\fontsize<7A>.
|
|||
|
/// </summary>
|
|||
|
private void ParseFontSize(FormattedText formattedText, int nestingLevel)
|
|||
|
{
|
|||
|
AssertSymbol(Symbol.FontSize);
|
|||
|
ReadCode();
|
|||
|
|
|||
|
AssertSymbol(Symbol.ParenLeft);
|
|||
|
ReadCode();
|
|||
|
//NYI: Check token for correct Unit format
|
|||
|
formattedText.Font.Size = Token;
|
|||
|
ReadCode();
|
|||
|
AssertSymbol(Symbol.ParenRight);
|
|||
|
ReadCode();
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
ParseFormattedText(formattedText.Elements, nestingLevel);
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\fontcolor<6F>.
|
|||
|
/// </summary>
|
|||
|
private void ParseFontColor(FormattedText formattedText, int nestingLevel)
|
|||
|
{
|
|||
|
AssertSymbol(Symbol.FontColor);
|
|||
|
ReadCode(); // read '('
|
|||
|
|
|||
|
AssertSymbol(Symbol.ParenLeft);
|
|||
|
ReadCode(); // read color token
|
|||
|
Color color = ParseColor();
|
|||
|
formattedText.Font.Color = color;
|
|||
|
AssertSymbol(Symbol.ParenRight);
|
|||
|
ReadCode();
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
ParseFormattedText(formattedText.Elements, nestingLevel);
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\symbol<6F> resp. <20>\(<28>.
|
|||
|
/// </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 <20>\chr<68>.
|
|||
|
/// </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>
|
|||
|
/// Parses the keyword <20>\field<6C>.
|
|||
|
/// </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 <20>\footnote<74>.
|
|||
|
/// </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 <20>\paragraph<70> is typically omitted.
|
|||
|
if (IsParagraphContent())
|
|||
|
{
|
|||
|
Paragraph paragraph = footnote.Elements.AddParagraph();
|
|||
|
ParseParagraphContent(footnote.Elements, paragraph);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ReadCode(); // read beyond '{'
|
|||
|
ParseDocumentElements(footnote.Elements, Symbol.Footnote);
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\hyperlink<6E>.
|
|||
|
/// </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 <20>\space<63>.
|
|||
|
/// </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);
|
|||
|
|
|||
|
// <20>\space<63> 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>
|
|||
|
/// Parses a page break in a document elements container.
|
|||
|
/// </summary>
|
|||
|
private void ParsePageBreak(DocumentElements elements)
|
|||
|
{
|
|||
|
AssertSymbol(Symbol.PageBreak);
|
|||
|
elements.AddPageBreak();
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\table<6C>.
|
|||
|
/// </summary>
|
|||
|
private void ParseTable(DocumentElements elements, Table table)
|
|||
|
{
|
|||
|
Table tbl = table;
|
|||
|
try
|
|||
|
{
|
|||
|
if (tbl == null)
|
|||
|
tbl = elements.AddTable();
|
|||
|
|
|||
|
MoveToCode();
|
|||
|
AssertSymbol(Symbol.Table);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
if (_scanner.Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(tbl);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
ReadCode();
|
|||
|
|
|||
|
// Table must start with <20>\columns<6E>...
|
|||
|
AssertSymbol(Symbol.Columns);
|
|||
|
ParseColumns(tbl);
|
|||
|
|
|||
|
// ...followed by <20>\rows<77>.
|
|||
|
AssertSymbol(Symbol.Rows);
|
|||
|
ParseRows(tbl);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
catch (DdlParserException ex)
|
|||
|
{
|
|||
|
ReportParserException(ex);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\columns<6E>.
|
|||
|
/// </summary>
|
|||
|
private void ParseColumns(Table table)
|
|||
|
{
|
|||
|
Debug.Assert(table != null);
|
|||
|
Debug.Assert(Symbol == Symbol.Columns);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(table.Columns);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
ReadCode();
|
|||
|
|
|||
|
bool loop = true;
|
|||
|
while (loop)
|
|||
|
{
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.Eof:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.BraceRight:
|
|||
|
loop = false;
|
|||
|
ReadCode();
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Column:
|
|||
|
ParseColumn(table.AddColumn());
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
AssertSymbol(Symbol.Column);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\column<6D>.
|
|||
|
/// </summary>
|
|||
|
private void ParseColumn(Column column)
|
|||
|
{
|
|||
|
Debug.Assert(column != null);
|
|||
|
Debug.Assert(Symbol == Symbol.Column);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(column);
|
|||
|
|
|||
|
// Read empty content
|
|||
|
if (Symbol == Symbol.BraceLeft)
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\rows<77>.
|
|||
|
/// </summary>
|
|||
|
private void ParseRows(Table table)
|
|||
|
{
|
|||
|
Debug.Assert(table != null);
|
|||
|
Debug.Assert(Symbol == Symbol.Rows);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(table.Rows);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
ReadCode();
|
|||
|
|
|||
|
bool loop = true;
|
|||
|
while (loop)
|
|||
|
{
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.Eof:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.BraceRight:
|
|||
|
ReadCode(); // read '}'
|
|||
|
loop = false;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Row:
|
|||
|
ParseRow(table.AddRow());
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
AssertSymbol(Symbol.Row);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\row<6F>.
|
|||
|
/// </summary>
|
|||
|
private void ParseRow(Row row)
|
|||
|
{
|
|||
|
Debug.Assert(row != null);
|
|||
|
Debug.Assert(Symbol == Symbol.Row);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(row);
|
|||
|
|
|||
|
if (Symbol == Symbol.BraceLeft)
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
|
|||
|
bool loop = true;
|
|||
|
int idx = 0;
|
|||
|
//int cells = row.Cells.Count;
|
|||
|
while (loop)
|
|||
|
{
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.Eof:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.BraceRight:
|
|||
|
loop = false;
|
|||
|
ReadCode();
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Cell:
|
|||
|
ParseCell(row[idx]);
|
|||
|
idx++;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\cell<6C>.
|
|||
|
/// </summary>
|
|||
|
private void ParseCell(Cell cell)
|
|||
|
{
|
|||
|
Debug.Assert(cell != null);
|
|||
|
Debug.Assert(Symbol == Symbol.Cell);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(cell);
|
|||
|
|
|||
|
// Empty cells without braces are valid.
|
|||
|
if (Symbol == Symbol.BraceLeft)
|
|||
|
{
|
|||
|
if (IsParagraphContent())
|
|||
|
{
|
|||
|
ParseParagraphContent(cell.Elements, null);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
if (Symbol != Symbol.BraceRight)
|
|||
|
ParseDocumentElements(cell.Elements, Symbol.Cell);
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode(); // read '}'
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\image<67>.
|
|||
|
/// </summary>
|
|||
|
private void ParseImage(Image image, bool paragraphContent)
|
|||
|
{
|
|||
|
// Future syntax by example
|
|||
|
// \image("Name")
|
|||
|
// \image("Name")[...]
|
|||
|
// \image{base64...} //NYI
|
|||
|
// \image[...]{base64...} //NYI
|
|||
|
Debug.Assert(image != null);
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
MoveToCode();
|
|||
|
AssertSymbol(Symbol.Image);
|
|||
|
ReadCode();
|
|||
|
|
|||
|
if (_scanner.Symbol == Symbol.ParenLeft)
|
|||
|
image.Name = ParseElementName();
|
|||
|
|
|||
|
if (_scanner.PeekSymbol() == Symbol.BracketLeft)
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
ParseAttributes(image, !paragraphContent);
|
|||
|
}
|
|||
|
else if (!paragraphContent)
|
|||
|
ReadCode(); // We are a part of a section, cell etc.; read beyond ')'.
|
|||
|
}
|
|||
|
catch (DdlParserException ex)
|
|||
|
{
|
|||
|
ReportParserException(ex);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\textframe<6D>.
|
|||
|
/// </summary>
|
|||
|
private void ParseTextFrame(DocumentElements elements)
|
|||
|
{
|
|||
|
Debug.Assert(elements != null);
|
|||
|
|
|||
|
TextFrame textFrame = elements.AddTextFrame();
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
if (_scanner.Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(textFrame);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
if (IsParagraphContent())
|
|||
|
{
|
|||
|
ParseParagraphContent(textFrame.Elements, null);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ReadCode(); // read '{'
|
|||
|
ParseDocumentElements(textFrame.Elements, Symbol.TextFrame);
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
catch (DdlParserException ex)
|
|||
|
{
|
|||
|
ReportParserException(ex);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void ParseBarcode(DocumentElements elements)
|
|||
|
{
|
|||
|
// Syntax:
|
|||
|
// 1. \barcode(Code)
|
|||
|
// 2. \barcode(Code)[...]
|
|||
|
// 3. \barcode(Code, Type)
|
|||
|
// 4. \barcode(Code, Type)[...]
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
AssertSymbol(Symbol.ParenLeft, DomMsgID.MissingParenLeft, GetSymbolText(Symbol.Barcode));
|
|||
|
ReadCode();
|
|||
|
AssertSymbol(Symbol.StringLiteral, DomMsgID.UnexpectedSymbol);
|
|||
|
|
|||
|
Barcode barcode = elements.AddBarcode();
|
|||
|
barcode.SetValue("Code", Token);
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.Comma)
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
AssertSymbol(Symbol.Identifier, DomMsgID.IdentifierExpected, Token);
|
|||
|
BarcodeType barcodeType = (BarcodeType)Enum.Parse(typeof(BarcodeType), Token, true);
|
|||
|
barcode.SetValue("type", barcodeType);
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.ParenRight, DomMsgID.MissingParenRight, GetSymbolText(Symbol.Barcode));
|
|||
|
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(barcode);
|
|||
|
//barcode->ConsistencyCheck(mInfoHandler->Infos());
|
|||
|
}
|
|||
|
catch (DdlParserException pe)
|
|||
|
{
|
|||
|
ReportParserException(pe);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\chart<72>.
|
|||
|
/// </summary>
|
|||
|
private void ParseChart(DocumentElements elements)
|
|||
|
{
|
|||
|
// Syntax:
|
|||
|
// 1. \chartarea(Type){...}
|
|||
|
// 2. \chartarea(Type)[...]{...}
|
|||
|
//
|
|||
|
// Usage of header-, bottom-, footer-, left- and rightarea are similar.
|
|||
|
|
|||
|
ChartType chartType = 0;
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode(); // read '('
|
|||
|
AssertSymbol(Symbol.ParenLeft, DomMsgID.MissingParenLeft, GetSymbolText(Symbol.Chart));
|
|||
|
|
|||
|
ReadCode(); // ChartType name
|
|||
|
AssertSymbol(Symbol.Identifier, DomMsgID.IdentifierExpected, Token);
|
|||
|
string chartTypeName = Token;
|
|||
|
|
|||
|
ReadCode(); // read ')'
|
|||
|
AssertSymbol(Symbol.ParenRight, DomMsgID.MissingParenRight, GetSymbolText(Symbol.Chart));
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
chartType = (ChartType)Enum.Parse(typeof(ChartType), chartTypeName, true);
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
ThrowParserException(ex, DomMsgID.UnknownChartType, chartTypeName);
|
|||
|
}
|
|||
|
|
|||
|
Chart chart = elements.AddChart(chartType);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(chart);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft, DomMsgID.MissingBraceLeft, GetSymbolText(Symbol.Chart));
|
|||
|
|
|||
|
ReadCode(); // read beyond '{'
|
|||
|
|
|||
|
bool fContinue = true;
|
|||
|
while (fContinue)
|
|||
|
{
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.Eof:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.BraceRight:
|
|||
|
fContinue = false;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.PlotArea:
|
|||
|
ParseArea(chart.PlotArea);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.HeaderArea:
|
|||
|
ParseArea(chart.HeaderArea);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.FooterArea:
|
|||
|
ParseArea(chart.FooterArea);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.TopArea:
|
|||
|
ParseArea(chart.TopArea);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.BottomArea:
|
|||
|
ParseArea(chart.BottomArea);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.LeftArea:
|
|||
|
ParseArea(chart.LeftArea);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.RightArea:
|
|||
|
ParseArea(chart.RightArea);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.XAxis:
|
|||
|
ParseAxes(chart.XAxis, Symbol);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.YAxis:
|
|||
|
ParseAxes(chart.YAxis, Symbol);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.ZAxis:
|
|||
|
ParseAxes(chart.ZAxis, Symbol);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Series:
|
|||
|
ParseSeries(chart.SeriesCollection.AddSeries());
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.XValues:
|
|||
|
ParseSeries(chart.XValues.AddXSeries());
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
catch (DdlParserException pe)
|
|||
|
{
|
|||
|
ReportParserException(pe);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\plotarea<65> inside a chart.
|
|||
|
/// </summary>
|
|||
|
private void ParseArea(PlotArea area)
|
|||
|
{
|
|||
|
// Syntax:
|
|||
|
// 1. \plotarea{...}
|
|||
|
// 2. \plotarea[...]{...} //???
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
{
|
|||
|
ParseAttributes(area, false);
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
if (Symbol != Symbol.BraceLeft)
|
|||
|
return;
|
|||
|
|
|||
|
bool fContinue = true;
|
|||
|
while (fContinue)
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.BraceRight:
|
|||
|
fContinue = false;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
// Alles ignorieren? Warnung ausgeben?
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
catch (DdlParserException pe)
|
|||
|
{
|
|||
|
ReportParserException(pe);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keywords <20>\headerarea<65>, <20>\toparea<65>, <20>\bottomarea<65>, <20>\footerarea<65>,
|
|||
|
/// <20>\leftarea<65> or <20>\rightarea<65> inside a chart.
|
|||
|
/// </summary>
|
|||
|
private void ParseArea(TextArea area)
|
|||
|
{
|
|||
|
// Syntax:
|
|||
|
// 1. \toparea{...}
|
|||
|
// 2. \toparea[...]{...}
|
|||
|
//
|
|||
|
// Usage of header-, bottom-, footer-, left- and rightarea are similar.
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
{
|
|||
|
ParseAttributes(area, false);
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
if (Symbol != Symbol.BraceLeft)
|
|||
|
return;
|
|||
|
|
|||
|
if (IsParagraphContent())
|
|||
|
ParseParagraphContent(area.Elements, null);
|
|||
|
else
|
|||
|
{
|
|||
|
ReadCode(); // read beyond '{'
|
|||
|
bool fContinue = true;
|
|||
|
while (fContinue)
|
|||
|
{
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.BraceRight:
|
|||
|
fContinue = false;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Legend:
|
|||
|
ParseLegend(area.AddLegend());
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Paragraph:
|
|||
|
ParseParagraph(area.Elements);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Table:
|
|||
|
ParseTable(null, area.AddTable());
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.TextFrame:
|
|||
|
ParseTextFrame(area.Elements);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Image:
|
|||
|
Image image = new Image();
|
|||
|
ParseImage(image, false);
|
|||
|
area.Elements.Add(image);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
catch (DdlParserException pe)
|
|||
|
{
|
|||
|
ReportParserException(pe);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keywords <20>\xaxis<69>, <20>\yaxis<69> or <20>\zaxis<69> inside a chart.
|
|||
|
/// </summary>
|
|||
|
private void ParseAxes(Axis axis, Symbol symbolAxis)
|
|||
|
{
|
|||
|
// Syntax:
|
|||
|
// 1. \xaxis[...]
|
|||
|
// 2. \xaxis[...]{...} //???
|
|||
|
//
|
|||
|
// Usage of yaxis and zaxis are similar.
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
{
|
|||
|
ParseAttributes(axis, false);
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
if (Symbol != Symbol.BraceLeft)
|
|||
|
return;
|
|||
|
|
|||
|
while (Symbol != Symbol.BraceRight)
|
|||
|
ReadCode();
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceRight, DomMsgID.MissingBraceRight, GetSymbolText(symbolAxis));
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
catch (DdlParserException pe)
|
|||
|
{
|
|||
|
ReportParserException(pe);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\series<65> inside a chart.
|
|||
|
/// </summary>
|
|||
|
private void ParseSeries(Series series)
|
|||
|
{
|
|||
|
// Syntax:
|
|||
|
// 1. \series{...}
|
|||
|
// 2. \series[...]{...}
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(series);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft, DomMsgID.MissingBraceLeft, GetSymbolText(Symbol.Series));
|
|||
|
ReadCode(); // read beyond '{'
|
|||
|
|
|||
|
bool fContinue = true;
|
|||
|
bool fFoundComma = true;
|
|||
|
while (fContinue)
|
|||
|
{
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.Eof:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.BraceRight:
|
|||
|
fContinue = false;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Comma:
|
|||
|
fFoundComma = true;
|
|||
|
ReadCode();
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Point:
|
|||
|
AssertCondition(fFoundComma, DomMsgID.MissingComma);
|
|||
|
ParsePoint(series.Add(0.0));
|
|||
|
fFoundComma = false;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Null:
|
|||
|
AssertCondition(fFoundComma, DomMsgID.MissingComma);
|
|||
|
series.AddBlank();
|
|||
|
fFoundComma = false;
|
|||
|
ReadCode();
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
AssertCondition(fFoundComma, DomMsgID.MissingComma);
|
|||
|
series.Add(_scanner.GetTokenValueAsReal());
|
|||
|
fFoundComma = false;
|
|||
|
ReadCode();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.BraceRight, DomMsgID.MissingBraceRight, GetSymbolText(Symbol.Series));
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
catch (DdlParserException pe)
|
|||
|
{
|
|||
|
ReportParserException(pe);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\xvalues<65> inside a chart.
|
|||
|
/// </summary>
|
|||
|
private void ParseSeries(XSeries series)
|
|||
|
{
|
|||
|
// Syntax:
|
|||
|
// 1. \xvalues{...}
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
AssertSymbol(Symbol.BraceLeft, DomMsgID.MissingBraceLeft, GetSymbolText(Symbol.Series));
|
|||
|
|
|||
|
bool fFoundComma = true;
|
|||
|
bool fContinue = true;
|
|||
|
while (fContinue)
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.Eof:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.BraceRight:
|
|||
|
fContinue = false;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Comma:
|
|||
|
fFoundComma = true;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Null:
|
|||
|
AssertCondition(fFoundComma, DomMsgID.MissingComma);
|
|||
|
series.AddBlank();
|
|||
|
fFoundComma = false;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.StringLiteral:
|
|||
|
case Symbol.IntegerLiteral:
|
|||
|
case Symbol.RealLiteral:
|
|||
|
case Symbol.HexIntegerLiteral:
|
|||
|
AssertCondition(fFoundComma, DomMsgID.MissingComma);
|
|||
|
series.Add(Token);
|
|||
|
fFoundComma = false;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.BraceRight, DomMsgID.MissingBraceRight, GetSymbolText(Symbol.Series));
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
catch (DdlParserException pe)
|
|||
|
{
|
|||
|
ReportParserException(pe);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\point<6E> inside a series.
|
|||
|
/// </summary>
|
|||
|
private void ParsePoint(Point point)
|
|||
|
{
|
|||
|
// Syntax:
|
|||
|
// 1. \point{...}
|
|||
|
// 2. \point[...]{...}
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
ParseAttributes(point);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft, DomMsgID.MissingBraceLeft, GetSymbolText(Symbol.Point));
|
|||
|
ReadCode(); // read beyond '{'
|
|||
|
point.Value = _scanner.GetTokenValueAsReal();
|
|||
|
|
|||
|
ReadCode(); // read '}'
|
|||
|
AssertSymbol(Symbol.BraceRight, DomMsgID.MissingBraceRight, GetSymbolText(Symbol.Point));
|
|||
|
ReadCode(); // read beyond '}'
|
|||
|
}
|
|||
|
catch (DdlParserException pe)
|
|||
|
{
|
|||
|
ReportParserException(pe);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the keyword <20>\legend<6E> inside a textarea.
|
|||
|
/// </summary>
|
|||
|
private void ParseLegend(Legend legend)
|
|||
|
{
|
|||
|
// Syntax:
|
|||
|
// 1. \legend
|
|||
|
// 2. \legend[...]
|
|||
|
// 3. \legend[...]{...}
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
ReadCode();
|
|||
|
if (Symbol == Symbol.BracketLeft)
|
|||
|
{
|
|||
|
ParseAttributes(legend, false);
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
// Empty legends are allowed.
|
|||
|
if (Symbol != Symbol.BraceLeft)
|
|||
|
return;
|
|||
|
|
|||
|
AdjustToNextBlock(); // consume/ignore all content
|
|||
|
}
|
|||
|
catch (DdlParserException pe)
|
|||
|
{
|
|||
|
ReportParserException(pe);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses an attribute declaration block enclosed in brackets <20>[<5B>]<5D>. If readNextSymbol is
|
|||
|
/// set to true, the closing bracket will be read.
|
|||
|
/// </summary>
|
|||
|
private void ParseAttributes(DocumentObject element, bool readNextSymbol)
|
|||
|
{
|
|||
|
AssertSymbol(Symbol.BracketLeft);
|
|||
|
ReadCode(); // read beyond '['
|
|||
|
|
|||
|
while (Symbol == Symbol.Identifier)
|
|||
|
ParseAttributeStatement(element);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BracketRight);
|
|||
|
|
|||
|
// Do not read ']' when parsing in paragraph content.
|
|||
|
if (readNextSymbol)
|
|||
|
ReadCode(); // read beyond ']'
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses an attribute declaration block enclosed in brackets <20>[<5B>]<5D>.
|
|||
|
/// </summary>
|
|||
|
private void ParseAttributes(DocumentObject element)
|
|||
|
{
|
|||
|
ParseAttributes(element, true);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses a single statement in an attribute declaration block.
|
|||
|
/// </summary>
|
|||
|
private void ParseAttributeStatement(DocumentObject doc)
|
|||
|
{
|
|||
|
// Syntax is easy
|
|||
|
// identifier: xxxxx
|
|||
|
// or
|
|||
|
// sequence of identifiers: xxx.yyy.zzz
|
|||
|
//
|
|||
|
// followed by: <20>=<3D>, <20>+=<3D>, <20>-=<3D>, or <20>{<7B>
|
|||
|
//
|
|||
|
// Parser of rhs depends on the type of the l-value.
|
|||
|
|
|||
|
if (doc == null)
|
|||
|
throw new ArgumentNullException("doc");
|
|||
|
string valueName = "";
|
|||
|
try
|
|||
|
{
|
|||
|
valueName = _scanner.Token;
|
|||
|
ReadCode();
|
|||
|
|
|||
|
// Resolve path, if it exists.
|
|||
|
object val;
|
|||
|
while (Symbol == Symbol.Dot)
|
|||
|
{
|
|||
|
#if DEBUG_
|
|||
|
if (valueName == "TabStops")
|
|||
|
valueName.GetType();
|
|||
|
#endif
|
|||
|
Debug.Assert(doc != null, "Make ReSharper happy.");
|
|||
|
val = doc.GetValue(valueName);
|
|||
|
if (val == null)
|
|||
|
{
|
|||
|
DocumentObject documentObject = doc;
|
|||
|
val = documentObject.CreateValue(valueName);
|
|||
|
doc.SetValue(valueName, val);
|
|||
|
}
|
|||
|
AssertCondition(val != null, DomMsgID.InvalidValueName, valueName);
|
|||
|
doc = val as DocumentObject;
|
|||
|
AssertCondition(doc != null, DomMsgID.SymbolIsNotAnObject, valueName);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
AssertCondition(Symbol == Symbol.Identifier, DomMsgID.InvalidValueName, _scanner.Token);
|
|||
|
valueName = _scanner.Token;
|
|||
|
AssertCondition(valueName[0] != '_', DomMsgID.NoAccess, _scanner.Token);
|
|||
|
|
|||
|
#if DEBUG_
|
|||
|
if (valueName == "TabStops")
|
|||
|
valueName.GetType();
|
|||
|
#endif
|
|||
|
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
Debug.Assert(doc != null, "Make ReSharper happy.");
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.Assign:
|
|||
|
//DomValueDescriptor is needed from assignment routine.
|
|||
|
ValueDescriptor pvd = doc.Meta[valueName];
|
|||
|
AssertCondition(pvd != null, DomMsgID.InvalidValueName, valueName);
|
|||
|
ParseAssign(doc, pvd);
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.PlusAssign:
|
|||
|
case Symbol.MinusAssign:
|
|||
|
// Hard-coded for TabStops only...
|
|||
|
if (!(doc is ParagraphFormat))
|
|||
|
ThrowParserException(DomMsgID.SymbolNotAllowed, _scanner.Token);
|
|||
|
if (String.Compare(valueName, "TabStops", StringComparison.OrdinalIgnoreCase) != 0)
|
|||
|
ThrowParserException(DomMsgID.InvalidValueForOperation, valueName, _scanner.Token);
|
|||
|
|
|||
|
ParagraphFormat paragraphFormat = (ParagraphFormat)doc;
|
|||
|
TabStops tabStops = paragraphFormat.TabStops;
|
|||
|
|
|||
|
if (true) // HACK in ParseAttributeStatement
|
|||
|
{
|
|||
|
bool fAddItem = Symbol == Symbol.PlusAssign;
|
|||
|
TabStop tabStop = new TabStop();
|
|||
|
|
|||
|
ReadCode();
|
|||
|
|
|||
|
if (Symbol == Symbol.BraceLeft)
|
|||
|
{
|
|||
|
ParseAttributeBlock(tabStop);
|
|||
|
}
|
|||
|
else if (Symbol == Symbol.StringLiteral || Symbol == Symbol.RealLiteral || Symbol == Symbol.IntegerLiteral)
|
|||
|
{
|
|||
|
// Special hack for tab stops...
|
|||
|
Unit unit = Token;
|
|||
|
tabStop.SetValue("Position", unit);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
else
|
|||
|
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
|
|||
|
|
|||
|
if (fAddItem)
|
|||
|
tabStops.AddTabStop(tabStop);
|
|||
|
else
|
|||
|
tabStops.RemoveTabStop(tabStop.Position);
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.BraceLeft:
|
|||
|
val = doc.GetValue(valueName);
|
|||
|
AssertCondition(val != null, DomMsgID.InvalidValueName, valueName);
|
|||
|
|
|||
|
DocumentObject doc2 = val as DocumentObject;
|
|||
|
if (doc2 != null)
|
|||
|
ParseAttributeBlock(doc2);
|
|||
|
else
|
|||
|
ThrowParserException(DomMsgID.SymbolIsNotAnObject, valueName);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ThrowParserException(DomMsgID.SymbolNotAllowed, _scanner.Token);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
catch (DdlParserException ex)
|
|||
|
{
|
|||
|
ReportParserException(ex);
|
|||
|
AdjustToNextBlock();
|
|||
|
}
|
|||
|
catch (ArgumentException e)
|
|||
|
{
|
|||
|
ReportParserException(e, DomMsgID.InvalidAssignment, valueName);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses an attribute declaration block enclosed in braces <20>{<7B>}<7D>.
|
|||
|
/// </summary>
|
|||
|
private void ParseAttributeBlock(DocumentObject element)
|
|||
|
{
|
|||
|
// Technically the same as ParseAttributes
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceLeft);
|
|||
|
ReadCode(); // move beyond '{'
|
|||
|
|
|||
|
while (Symbol == Symbol.Identifier)
|
|||
|
ParseAttributeStatement(element);
|
|||
|
|
|||
|
AssertSymbol(Symbol.BraceRight);
|
|||
|
ReadCode(); // move beyond '}'
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses an assign statement in an attribute declaration block.
|
|||
|
/// </summary>
|
|||
|
private void ParseAssign(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
if (dom == null)
|
|||
|
throw new ArgumentNullException("dom");
|
|||
|
if (vd == null)
|
|||
|
throw new ArgumentNullException("vd");
|
|||
|
|
|||
|
if (Symbol == Symbol.Assign)
|
|||
|
ReadCode();
|
|||
|
|
|||
|
Type valType = vd.ValueType;
|
|||
|
try
|
|||
|
{
|
|||
|
if (valType == typeof(string))
|
|||
|
ParseStringAssignment(dom, vd);
|
|||
|
else if (valType == typeof(int))
|
|||
|
ParseIntegerAssignment(dom, vd);
|
|||
|
else if (valType == typeof(Unit))
|
|||
|
ParseUnitAssignment(dom, vd);
|
|||
|
else if (valType == typeof(double) || valType == typeof(float))
|
|||
|
ParseRealAssignment(dom, vd);
|
|||
|
else if (valType == typeof(bool))
|
|||
|
ParseBoolAssignment(dom, vd);
|
|||
|
#if !NETFX_CORE
|
|||
|
else if (typeof(Enum).IsAssignableFrom(valType))
|
|||
|
#else
|
|||
|
else if (typeof(Enum).GetTypeInfo().IsAssignableFrom(valType.GetTypeInfo()))
|
|||
|
#endif
|
|||
|
ParseEnumAssignment(dom, vd);
|
|||
|
else if (valType == typeof(Color))
|
|||
|
ParseColorAssignment(dom, vd);
|
|||
|
#if !NETFX_CORE
|
|||
|
else if (typeof(ValueType).IsAssignableFrom(valType))
|
|||
|
#else
|
|||
|
else if (typeof(ValueType).GetTypeInfo().IsAssignableFrom(valType.GetTypeInfo()))
|
|||
|
#endif
|
|||
|
{
|
|||
|
ParseValueTypeAssignment(dom, vd);
|
|||
|
}
|
|||
|
#if !NETFX_CORE
|
|||
|
else if (typeof(DocumentObject).IsAssignableFrom(valType))
|
|||
|
#else
|
|||
|
else if (typeof(DocumentObject).GetTypeInfo().IsAssignableFrom(valType.GetTypeInfo()))
|
|||
|
#endif
|
|||
|
{
|
|||
|
ParseDocumentObjectAssignment(dom, vd);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
AdjustToNextStatement();
|
|||
|
ThrowParserException(DomMsgID.InvalidType, vd.ValueType.Name, vd.ValueName);
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
ReportParserException(ex, DomMsgID.InvalidAssignment, vd.ValueName);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the assignment to a boolean l-value.
|
|||
|
/// </summary>
|
|||
|
private void ParseBoolAssignment(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
AssertCondition(Symbol == Symbol.True || Symbol == Symbol.False, DomMsgID.BoolExpected,
|
|||
|
_scanner.Token);
|
|||
|
|
|||
|
dom.SetValue(vd.ValueName, Symbol == Symbol.True);
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the assignment to an integer l-value.
|
|||
|
/// </summary>
|
|||
|
private void ParseIntegerAssignment(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral || Symbol == Symbol.StringLiteral,
|
|||
|
DomMsgID.IntegerExpected, Token);
|
|||
|
|
|||
|
int n = Int32.Parse(_scanner.Token, CultureInfo.InvariantCulture);
|
|||
|
dom.SetValue(vd.ValueName, n);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the assignment to a floating point l-value.
|
|||
|
/// </summary>
|
|||
|
private void ParseRealAssignment(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
AssertCondition(Symbol == Symbol.RealLiteral || Symbol == Symbol.IntegerLiteral || Symbol == Symbol.StringLiteral,
|
|||
|
DomMsgID.RealExpected, _scanner.Token);
|
|||
|
|
|||
|
double r = double.Parse(_scanner.Token, CultureInfo.InvariantCulture);
|
|||
|
dom.SetValue(vd.ValueName, r);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the assignment to a Unit l-value.
|
|||
|
/// </summary>
|
|||
|
private void ParseUnitAssignment(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
AssertCondition(Symbol == Symbol.RealLiteral || Symbol == Symbol.IntegerLiteral || Symbol == Symbol.StringLiteral,
|
|||
|
DomMsgID.RealExpected, _scanner.Token);
|
|||
|
|
|||
|
Unit unit = Token;
|
|||
|
dom.SetValue(vd.ValueName, unit);
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the assignment to a string l-value.
|
|||
|
/// </summary>
|
|||
|
private void ParseStringAssignment(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
AssertCondition(Symbol == Symbol.StringLiteral, DomMsgID.StringExpected, _scanner.Token);
|
|||
|
|
|||
|
vd.SetValue(dom, Token); //dom.SetValue(vd.ValueName, scanner.Token);
|
|||
|
|
|||
|
ReadCode(); // read next token
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the assignment to an enum l-value.
|
|||
|
/// </summary>
|
|||
|
private void ParseEnumAssignment(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
AssertSymbol(Symbol.Identifier, DomMsgID.IdentifierExpected, _scanner.Token);
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
object val = Enum.Parse(vd.ValueType, Token, true);
|
|||
|
dom.SetValue(vd.ValueName, val);
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
ThrowParserException(ex, DomMsgID.InvalidEnum, _scanner.Token, vd.ValueName);
|
|||
|
}
|
|||
|
|
|||
|
ReadCode(); // read next token
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the assignment to a struct (i.e. LeftPosition) l-value.
|
|||
|
/// </summary>
|
|||
|
private void ParseValueTypeAssignment(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
object val = vd.GetValue(dom, GV.ReadWrite);
|
|||
|
try
|
|||
|
{
|
|||
|
INullableValue ival = (INullableValue)val;
|
|||
|
ival.SetValue(Token);
|
|||
|
dom.SetValue(vd.ValueName, val);
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
ReportParserException(ex, DomMsgID.InvalidAssignment, vd.ValueName);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the assignment to a DocumentObject l-value.
|
|||
|
/// </summary>
|
|||
|
private void ParseDocumentObjectAssignment(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
// Create value if it does not exist
|
|||
|
object val = vd.GetValue(dom, GV.ReadWrite);
|
|||
|
//DocumentObject docObj = (DocumentObject)val;
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
if (Symbol == Symbol.Null)
|
|||
|
{
|
|||
|
//string name = vd.ValueName;
|
|||
|
Type type = vd.ValueType;
|
|||
|
if (typeof(Border) == type)
|
|||
|
((Border)val).Clear();
|
|||
|
else if (typeof(Borders) == type)
|
|||
|
((Borders)val).ClearAll();
|
|||
|
else if (typeof(Shading) == type)
|
|||
|
((Shading)val).Clear();
|
|||
|
else if (typeof(TabStops) == type)
|
|||
|
{
|
|||
|
TabStops tabStops = (TabStops)vd.GetValue(dom, GV.ReadWrite);
|
|||
|
tabStops.ClearAll();
|
|||
|
}
|
|||
|
else
|
|||
|
ThrowParserException(DomMsgID.NullAssignmentNotSupported, vd.ValueName);
|
|||
|
|
|||
|
ReadCode();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
throw new Exception("Case: TopPosition");
|
|||
|
//dom.SetValue(vd.ValueName, docObj);
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
ReportParserException(ex, DomMsgID.InvalidAssignment, vd.ValueName);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the assignment to a Value l-value.
|
|||
|
/// </summary>
|
|||
|
private void ParseValueAssignment(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
// What ever it is, send it to SetValue.
|
|||
|
dom.SetValue(vd.ValueName, Token);
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
ThrowParserException(ex, DomMsgID.InvalidEnum, _scanner.Token, vd.ValueName);
|
|||
|
}
|
|||
|
|
|||
|
ReadCode(); // read next token
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses the assignment to a Color l-value.
|
|||
|
/// </summary>
|
|||
|
private void ParseColorAssignment(DocumentObject dom, ValueDescriptor vd)
|
|||
|
{
|
|||
|
object val = vd.GetValue(dom, GV.ReadWrite);
|
|||
|
Color color = ParseColor();
|
|||
|
dom.SetValue(vd.ValueName, color);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses a color. It can be <20>green<65>, <20>123456<35>, <20>0xFFABCDEF<45>,
|
|||
|
/// <20>RGB(r, g, b)<29>, <20>CMYK(c, m, y, k)<29>, <20>CMYK(a, c, m, y, k)<29>, <20>GRAY(g)<29>, or <20>"MyColor"<22>.
|
|||
|
/// </summary>
|
|||
|
private Color ParseColor()
|
|||
|
{
|
|||
|
MoveToCode();
|
|||
|
Color color = Color.Empty;
|
|||
|
if (Symbol == Symbol.Identifier)
|
|||
|
{
|
|||
|
switch (Token)
|
|||
|
{
|
|||
|
case "RGB":
|
|||
|
color = ParseRGB();
|
|||
|
break;
|
|||
|
|
|||
|
case "CMYK":
|
|||
|
color = ParseCMYK();
|
|||
|
break;
|
|||
|
|
|||
|
case "HSB":
|
|||
|
throw new NotImplementedException("ParseColor(HSB)");
|
|||
|
|
|||
|
case "Lab":
|
|||
|
throw new NotImplementedException("ParseColor(Lab)");
|
|||
|
|
|||
|
case "GRAY":
|
|||
|
color = ParseGray();
|
|||
|
break;
|
|||
|
|
|||
|
default: // Must be color enum
|
|||
|
try
|
|||
|
{
|
|||
|
color = Color.Parse(Token);
|
|||
|
ReadCode(); // read token
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
ThrowParserException(ex, DomMsgID.InvalidColor, _scanner.Token);
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else if (Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral)
|
|||
|
{
|
|||
|
color = new Color(_scanner.GetTokenValueAsUInt());
|
|||
|
ReadCode(); // read beyond literal
|
|||
|
}
|
|||
|
else if (Symbol == Symbol.StringLiteral)
|
|||
|
{
|
|||
|
throw new NotImplementedException("ParseColor(color-name)");
|
|||
|
}
|
|||
|
else
|
|||
|
ThrowParserException(DomMsgID.StringExpected, _scanner.Token);
|
|||
|
return color;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses <20>RGB(r, g, b)<29>.
|
|||
|
/// </summary>
|
|||
|
private Color ParseRGB()
|
|||
|
{
|
|||
|
uint r, g, b;
|
|||
|
ReadCode(); // read '('
|
|||
|
AssertSymbol(Symbol.ParenLeft);
|
|||
|
|
|||
|
ReadCode(); // read red value
|
|||
|
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral,
|
|||
|
DomMsgID.IntegerExpected, _scanner.Token);
|
|||
|
r = _scanner.GetTokenValueAsUInt();
|
|||
|
AssertCondition(r >= 0 && r <= 255, DomMsgID.InvalidRange, "0 - 255");
|
|||
|
|
|||
|
ReadCode(); // read ','
|
|||
|
AssertSymbol(Symbol.Comma);
|
|||
|
|
|||
|
ReadCode(); // read green value
|
|||
|
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral,
|
|||
|
DomMsgID.IntegerExpected, _scanner.Token);
|
|||
|
g = _scanner.GetTokenValueAsUInt();
|
|||
|
AssertCondition(g >= 0 && g <= 255, DomMsgID.InvalidRange, "0 - 255");
|
|||
|
|
|||
|
ReadCode(); // read ','
|
|||
|
AssertSymbol(Symbol.Comma);
|
|||
|
|
|||
|
ReadCode(); // read blue value
|
|||
|
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral,
|
|||
|
DomMsgID.IntegerExpected, _scanner.Token);
|
|||
|
b = _scanner.GetTokenValueAsUInt();
|
|||
|
AssertCondition(b >= 0 && b <= 255, DomMsgID.InvalidRange, "0 - 255");
|
|||
|
|
|||
|
ReadCode(); // read ')'
|
|||
|
AssertSymbol(Symbol.ParenRight);
|
|||
|
|
|||
|
ReadCode(); // read next token
|
|||
|
|
|||
|
return new Color(0xFF000000 | (r << 16) | (g << 8) | b);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses <20>CMYK(c, m, y, k)<29> or <20>CMYK(a, c, m, y, k)<29>.
|
|||
|
/// </summary>
|
|||
|
private Color ParseCMYK()
|
|||
|
{
|
|||
|
ReadCode(); // read '('
|
|||
|
AssertSymbol(Symbol.ParenLeft);
|
|||
|
|
|||
|
ReadCode(); // read v1 value
|
|||
|
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.RealLiteral,
|
|||
|
DomMsgID.NumberExpected, _scanner.Token);
|
|||
|
double v1 = _scanner.GetTokenValueAsReal();
|
|||
|
AssertCondition(v1 >= 0.0f && v1 <= 100.0f, DomMsgID.InvalidRange, "0.0 - 100.0");
|
|||
|
|
|||
|
ReadCode(); // read ','
|
|||
|
AssertSymbol(Symbol.Comma);
|
|||
|
|
|||
|
ReadCode(); // read v2 value
|
|||
|
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.RealLiteral,
|
|||
|
DomMsgID.NumberExpected, _scanner.Token);
|
|||
|
double v2 = _scanner.GetTokenValueAsReal();
|
|||
|
AssertCondition(v2 >= 0.0f && v2 <= 100.0f, DomMsgID.InvalidRange, "0.0 - 100.0");
|
|||
|
|
|||
|
ReadCode(); // read ','
|
|||
|
AssertSymbol(Symbol.Comma);
|
|||
|
|
|||
|
ReadCode(); // read v3 value
|
|||
|
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.RealLiteral,
|
|||
|
DomMsgID.NumberExpected, _scanner.Token);
|
|||
|
double v3 = _scanner.GetTokenValueAsReal();
|
|||
|
AssertCondition(v3 >= 0.0f && v3 <= 100.0f, DomMsgID.InvalidRange, "0.0 - 100.0");
|
|||
|
|
|||
|
ReadCode(); // read ','
|
|||
|
AssertSymbol(Symbol.Comma);
|
|||
|
|
|||
|
ReadCode(); // read v4 value
|
|||
|
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.RealLiteral,
|
|||
|
DomMsgID.NumberExpected, _scanner.Token);
|
|||
|
double v4 = _scanner.GetTokenValueAsReal();
|
|||
|
AssertCondition(v4 >= 0.0f && v4 <= 100.0, DomMsgID.InvalidRange, "0.0 - 100.0");
|
|||
|
|
|||
|
ReadCode(); // read ')' or ','
|
|||
|
bool hasAlpha = false;
|
|||
|
double v5 = 0;
|
|||
|
if (Symbol == Symbol.Comma)
|
|||
|
{
|
|||
|
hasAlpha = true;
|
|||
|
ReadCode(); // read v5 value
|
|||
|
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.RealLiteral,
|
|||
|
DomMsgID.NumberExpected, _scanner.Token);
|
|||
|
v5 = _scanner.GetTokenValueAsReal();
|
|||
|
AssertCondition(v5 >= 0.0f && v5 <= 100.0, DomMsgID.InvalidRange, "0.0 - 100.0");
|
|||
|
|
|||
|
ReadCode(); // read ')'
|
|||
|
}
|
|||
|
AssertSymbol(Symbol.ParenRight);
|
|||
|
|
|||
|
ReadCode(); // read next token
|
|||
|
|
|||
|
double a, c, m, y, k;
|
|||
|
if (hasAlpha)
|
|||
|
{
|
|||
|
a = v1; c = v2; m = v3; y = v4; k = v5;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
a = 100.0; c = v1; m = v2; y = v3; k = v4;
|
|||
|
}
|
|||
|
return Color.FromCmyk(a, c, m, y, k);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Parses <20>GRAY(g)<29>.
|
|||
|
/// </summary>
|
|||
|
private Color ParseGray()
|
|||
|
{
|
|||
|
ReadCode(); // read '('
|
|||
|
AssertSymbol(Symbol.ParenLeft);
|
|||
|
|
|||
|
ReadCode(); // read gray value
|
|||
|
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral,
|
|||
|
DomMsgID.IntegerExpected, _scanner.Token);
|
|||
|
double gray = _scanner.GetTokenValueAsReal();
|
|||
|
AssertCondition(gray >= 0.0f && gray <= 100.0f, DomMsgID.InvalidRange, "0.0 - 100.0");
|
|||
|
|
|||
|
ReadCode(); // read ')'
|
|||
|
AssertSymbol(Symbol.ParenRight);
|
|||
|
|
|||
|
ReadCode(); // read next token
|
|||
|
|
|||
|
uint g = (uint)((1 - gray / 100.0) * 255 + 0.5);
|
|||
|
return new Color(0xff000000 + (g << 16) + (g << 8) + g);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Determines the name/text of the given symbol.
|
|||
|
/// </summary>
|
|||
|
private string GetSymbolText(Symbol docSym)
|
|||
|
{
|
|||
|
return KeyWords.NameFromSymbol(docSym);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns whether the specified type is a valid SpaceType.
|
|||
|
/// </summary>
|
|||
|
private bool IsSpaceType(string type)
|
|||
|
{
|
|||
|
if (type == null)
|
|||
|
throw new ArgumentNullException("type");
|
|||
|
if (type == "")
|
|||
|
throw new ArgumentException("type");
|
|||
|
|
|||
|
if (Enum.IsDefined(typeof(SymbolName), type))
|
|||
|
{
|
|||
|
SymbolName symbolName = (SymbolName)Enum.Parse(typeof(SymbolName), type, false); // symbols are case sensitive
|
|||
|
switch (symbolName)
|
|||
|
{
|
|||
|
case SymbolName.Blank:
|
|||
|
case SymbolName.Em:
|
|||
|
//case SymbolName.Em4: // same as SymbolName.EmQuarter
|
|||
|
case SymbolName.EmQuarter:
|
|||
|
case SymbolName.En:
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Returns whether the specified type is a valid enum for \symbol.
|
|||
|
/// </summary>
|
|||
|
private bool IsSymbolType(string type)
|
|||
|
{
|
|||
|
if (type == null)
|
|||
|
throw new ArgumentNullException("type");
|
|||
|
if (type == "")
|
|||
|
throw new ArgumentException("type");
|
|||
|
|
|||
|
if (Enum.IsDefined(typeof(SymbolName), type))
|
|||
|
{
|
|||
|
SymbolName symbolName = (SymbolName)Enum.Parse(typeof(SymbolName), type, false); // symbols are case sensitive
|
|||
|
switch (symbolName)
|
|||
|
{
|
|||
|
case SymbolName.Euro:
|
|||
|
case SymbolName.Copyright:
|
|||
|
case SymbolName.Trademark:
|
|||
|
case SymbolName.RegisteredTrademark:
|
|||
|
case SymbolName.Bullet:
|
|||
|
case SymbolName.Not:
|
|||
|
case SymbolName.EmDash:
|
|||
|
case SymbolName.EnDash:
|
|||
|
case SymbolName.NonBreakableBlank:
|
|||
|
//case SymbolName.HardBlank: //same as SymbolName.NonBreakableBlank:
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// If cond is evaluated to false, a DdlParserException with the specified error will be thrown.
|
|||
|
/// </summary>
|
|||
|
private void AssertCondition(bool cond, DomMsgID error, params object[] args)
|
|||
|
{
|
|||
|
if (!cond)
|
|||
|
ThrowParserException(error, args);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// If current symbol is not equal symbol a DdlParserException will be thrown.
|
|||
|
/// </summary>
|
|||
|
private void AssertSymbol(Symbol symbol)
|
|||
|
{
|
|||
|
if (Symbol != symbol)
|
|||
|
ThrowParserException(DomMsgID.SymbolExpected, KeyWords.NameFromSymbol(symbol), Token);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// If current symbol is not equal symbol a DdlParserException with the specified message id
|
|||
|
/// will be thrown.
|
|||
|
/// </summary>
|
|||
|
private void AssertSymbol(Symbol symbol, DomMsgID err)
|
|||
|
{
|
|||
|
if (Symbol != symbol)
|
|||
|
ThrowParserException(err, KeyWords.NameFromSymbol(symbol), Token);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// If current symbol is not equal symbol a DdlParserException with the specified message id
|
|||
|
/// will be thrown.
|
|||
|
/// </summary>
|
|||
|
private void AssertSymbol(Symbol symbol, DomMsgID err, params object[] parms)
|
|||
|
{
|
|||
|
if (Symbol != symbol)
|
|||
|
ThrowParserException(err, KeyWords.NameFromSymbol(symbol), parms);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Creates an ErrorInfo based on the given errorlevel, error and parms and adds it to the ErrorManager2.
|
|||
|
/// </summary>
|
|||
|
private void ReportParserInfo(DdlErrorLevel level, DomMsgID errorCode, params string[] parms)
|
|||
|
{
|
|||
|
string message = DomSR.FormatMessage(errorCode, parms);
|
|||
|
DdlReaderError error = new DdlReaderError(level, message, (int)errorCode,
|
|||
|
_scanner.DocumentFileName, _scanner.CurrentLine, _scanner.CurrentLinePos);
|
|||
|
|
|||
|
_errors.AddError(error);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Creates an ErrorInfo based on the given error and parms and adds it to the ErrorManager2.
|
|||
|
/// </summary>
|
|||
|
private void ReportParserException(DomMsgID error, params string[] parms)
|
|||
|
{
|
|||
|
ReportParserException(null, error, parms);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Adds the ErrorInfo from the ErrorInfoException2 to the ErrorManager2.
|
|||
|
/// </summary>
|
|||
|
private void ReportParserException(DdlParserException ex)
|
|||
|
{
|
|||
|
_errors.AddError(ex.Error);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Creates an ErrorInfo based on the given inner exception, error and parms and adds it to the ErrorManager2.
|
|||
|
/// </summary>
|
|||
|
private void ReportParserException(Exception innerException, DomMsgID errorCode, params string[] parms)
|
|||
|
{
|
|||
|
string message = "";
|
|||
|
if (innerException != null)
|
|||
|
message = ": " + innerException;
|
|||
|
|
|||
|
message += DomSR.FormatMessage(errorCode, parms);
|
|||
|
DdlReaderError error = new DdlReaderError(DdlErrorLevel.Error, message, (int)errorCode,
|
|||
|
_scanner.DocumentFileName, _scanner.CurrentLine, _scanner.CurrentLinePos);
|
|||
|
|
|||
|
_errors.AddError(error);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Creates an ErrorInfo based on the DomMsgID and the specified parameters.
|
|||
|
/// Throws a DdlParserException with that ErrorInfo.
|
|||
|
/// </summary>
|
|||
|
private void ThrowParserException(DomMsgID errorCode, params object[] parms)
|
|||
|
{
|
|||
|
string message = DomSR.FormatMessage(errorCode, parms);
|
|||
|
DdlReaderError error = new DdlReaderError(DdlErrorLevel.Error, message, (int)errorCode,
|
|||
|
_scanner.DocumentFileName, _scanner.CurrentLine, _scanner.CurrentLinePos);
|
|||
|
|
|||
|
throw new DdlParserException(error);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Determines the error message based on the DomMsgID and the parameters.
|
|||
|
/// Throws a DdlParserException with that error message and the Exception as the inner exception.
|
|||
|
/// </summary>
|
|||
|
private void ThrowParserException(Exception innerException, DomMsgID errorCode, params object[] parms)
|
|||
|
{
|
|||
|
string message = DomSR.FormatMessage(errorCode, parms);
|
|||
|
throw new DdlParserException(message, innerException);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Used for exception handling. Sets the DDL stream to the next valid position behind
|
|||
|
/// the current block.
|
|||
|
/// </summary>
|
|||
|
private void AdjustToNextBlock()
|
|||
|
{
|
|||
|
bool skipClosingBraceOrBracket = (Symbol == Symbol.BraceLeft || Symbol == Symbol.BracketLeft);
|
|||
|
ReadCode();
|
|||
|
|
|||
|
bool finish = false;
|
|||
|
while (!finish)
|
|||
|
{
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.BraceLeft:
|
|||
|
case Symbol.BracketLeft:
|
|||
|
AdjustToNextBlock();
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.BraceRight:
|
|||
|
case Symbol.BracketRight:
|
|||
|
if (skipClosingBraceOrBracket)
|
|||
|
ReadCode();
|
|||
|
finish = true;
|
|||
|
break;
|
|||
|
|
|||
|
case Symbol.Eof:
|
|||
|
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
AdjustToNextStatement();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Used for exception handling. Sets the DDL stream to the next valid position behind
|
|||
|
/// the current statement.
|
|||
|
/// </summary>
|
|||
|
private void AdjustToNextStatement()
|
|||
|
{
|
|||
|
bool finish = false;
|
|||
|
while (!finish)
|
|||
|
{
|
|||
|
switch (Symbol)
|
|||
|
{
|
|||
|
case Symbol.Assign:
|
|||
|
//read one more symbol
|
|||
|
ReadCode();
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
ReadCode();
|
|||
|
finish = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Shortcut for scanner.ReadCode().
|
|||
|
/// Reads the next DDL token. Comments are ignored.
|
|||
|
/// </summary>
|
|||
|
private Symbol ReadCode()
|
|||
|
{
|
|||
|
return _scanner.ReadCode();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Shortcut for scanner.ReadText().
|
|||
|
/// Reads either text or \keyword from current position.
|
|||
|
/// </summary>
|
|||
|
private Symbol ReadText(bool rootLevel)
|
|||
|
{
|
|||
|
return _scanner.ReadText(rootLevel);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Shortcut for scanner.MoveToCode().
|
|||
|
/// Moves to the next DDL token if Symbol is not set to a valid position.
|
|||
|
/// </summary>
|
|||
|
private void MoveToCode()
|
|||
|
{
|
|||
|
_scanner.MoveToCode();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Shortcut for scanner.MoveToParagraphContent().
|
|||
|
/// Moves to the first character the content of a paragraph starts with. Empty lines
|
|||
|
/// and comments are skipped. Returns true if such a character exists, and false if the
|
|||
|
/// paragraph ends without content.
|
|||
|
/// </summary>
|
|||
|
public bool MoveToParagraphContent()
|
|||
|
{
|
|||
|
return _scanner.MoveToParagraphContent();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Shortcut for scanner.MoveToNextParagraphContentLine().
|
|||
|
/// Moves to the first character of the content of a paragraph beyond an EOL.
|
|||
|
/// Returns true if such a character exists and belongs to the current paragraph.
|
|||
|
/// Returns false if a new line (at root level) or '}' occurs. If a new line caused
|
|||
|
/// the end of the paragraph, the DDL cursor is moved to the next valid content
|
|||
|
/// character or '}' respectively.
|
|||
|
/// </summary>
|
|||
|
public bool MoveToNextParagraphContentLine(bool rootLevel)
|
|||
|
{
|
|||
|
return _scanner.MoveToNextParagraphContentLine(rootLevel);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the current symbol from the scanner.
|
|||
|
/// </summary>
|
|||
|
private Symbol Symbol
|
|||
|
{
|
|||
|
get { return _scanner.Symbol; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the current token from the scanner.
|
|||
|
/// </summary>
|
|||
|
private string Token
|
|||
|
{
|
|||
|
get { return _scanner.Token; }
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets the current token type from the scanner.
|
|||
|
/// </summary>
|
|||
|
private TokenType TokenType
|
|||
|
{
|
|||
|
get { return _scanner.TokenType; }
|
|||
|
}
|
|||
|
|
|||
|
private readonly DdlScanner _scanner;
|
|||
|
private readonly DdlReaderErrors _errors;
|
|||
|
}
|
|||
|
}
|