/// <summary>
        /// Parse preabule and fill class properties by parsed values
        /// </summary>
        /// <param name="preambule">Preambule node</param>
        public void Parse(Node preambule)
        {
            if (preambule.Type != "preambule")
                throw new ArgumentException("Function accepts only preambule node type.");

            // process preambule nodes
            foreach (Node node in preambule.Children)
            {
                switch (node.Type)
                {
                    // title page settings
                    case "author":
                    case "title":
                    case "date":
                    case "institute":
                        TitlepageSettings[node.Type] = node.Children;
                        break;
                    case "graphicspath":
                        foreach (Node child in node.Children)
                        {
                            if (child.Type == "path" && (child.Content as string).Length > 0)
                            {
                                string path = Path.Combine(InputDir, child.Content as string);

                                // add path only if directory exists
                                if(Directory.Exists(path))
                                    GraphicsPath.Add(path);
                            }
                        }
                        break;
                    case "usepackage":
                        if (node.Content as string == "inputenc" && node.OptionalParams != "utf8")
                            Messenger.Instance.SendMessage("Unsupported code page, some characters may be broken", Contract.MessageLevel.WARNING);
                        break;
                    // unknown or invalid node -> ignore
                    default:
                        break;
                }
            }
        }
        /// <summary>
        /// Generate table from its node
        /// </summary>
        /// <param name="tableNode">Table node</param>
        /// <param name="tableShape">output - Shape of generated table (used for reshaper), null if no table was generated</param>
        /// <returns>true if completed; false if paused</returns>
        private bool GenerateTable(Node tableNode, out PowerPoint.Shape tableShape)
        {
            int rows = 0, cols = 0;
            tableShape = null;

            TabularSettings settings = TabularSettings.Parse(tableNode.Content as string);

            cols = settings.Columns.Count;

            // count table rows
            foreach (Node node in tableNode.Children)
            {
                if (node.Type == "tablerow")
                    rows++;
            } // counted number of rows can be exactly one row greater than actual value (last row is empty)

            if (cols == 0 || rows == 0) // no columns or rows -> don't create table
                return true;

            // create table shape with "rows - 1" rows but at least one row; also create table with extreme width so we can resize it down
            tableShape = _slide.Shapes.AddTable(((rows - 1) > 0 ? rows - 1 : rows), cols, 36.0f, _bottomShapeBorder + 5.0f, cols * 1000.0f);
            // style without background and borders
            tableShape.Table.ApplyStyle("2D5ABB26-0587-4C30-8999-92F81FD0307C");

            int rowCounter = 0, columnCounter = 0;

            Stack<Node> nodes = new Stack<Node>();

            Node currentNode;

            PowerPoint.Shape shape; // cell shape

            // skip expanding children to stack
            bool skip = false;

            // pause processing variables
            bool paused = false;
            int pausedAfter = 0;

            foreach (Node node in tableNode.Children)
            {
                columnCounter = 0;

                if (node.Type == "tablerow")
                {
                    rowCounter++;

                    // check if we will generate last row
                    if (rowCounter == rows && rowCounter != 1)
                    {
                        if (node.Children.Count == 1 && node.Children[0].Children.Count == 0)
                            continue;
                        else
                            tableShape.Table.Rows.Add();
                    }

                    foreach (Node rowcontent in node.Children)
                    {
                        if (rowcontent.Type == "tablecolumn" || rowcontent.Type == "tablecolumn_merged")
                        {
                            columnCounter++;

                            if (columnCounter > cols)
                                throw new DocumentBuilderException("Invalid table definition.");

                            // get cell shape
                            shape = tableShape.Table.Cell(rowCounter, columnCounter).Shape;

                            // set cell alignment
                            switch (settings.Columns[columnCounter-1].alignment)
                            {
                                case 'l':
                                    shape.TextFrame2.TextRange.ParagraphFormat.Alignment = MsoParagraphAlignment.msoAlignLeft;
                                    break;
                                case 'c':
                                    shape.TextFrame2.TextRange.ParagraphFormat.Alignment = MsoParagraphAlignment.msoAlignCenter;
                                    break;
                                case 'r':
                                    shape.TextFrame2.TextRange.ParagraphFormat.Alignment = MsoParagraphAlignment.msoAlignRight;
                                    break;
                                case 'p':
                                    shape.TextFrame2.TextRange.ParagraphFormat.Alignment = MsoParagraphAlignment.msoAlignJustify;
                                    break;
                                default:
                                    break;
                            }

                            _format.Invalidate();

                            // copy column content to stack
                            foreach (Node item in rowcontent.Children.Reverse<Node>())
                            {
                                nodes.Push(item);
                            }

                            // process nodes on stack
                            while (nodes.Count != 0)
                            {
                                currentNode = nodes.Pop();

                                skip = false;

                                // process node depending on its type
                                switch (currentNode.Type)
                                {
                                    case "string":
                                        _format.AppendText(shape, currentNode.Content as string);
                                        break;
                                    case "paragraph":
                                        _format.AppendText(shape, "\r");
                                        break;
                                    case "pause":
                                        if (!paused && Pause())
                                        {
                                            paused = true;
                                            if (columnCounter == 1 && shape.TextFrame2.TextRange.Text.Length == 0)
                                                pausedAfter = rowCounter - 1;
                                            else
                                                pausedAfter = rowCounter;
                                        }
                                        break;
                                    case "numberedlist":
                                    case "bulletlist":
                                    case "descriptionlist":
                                    case "image":
                                    case "table":
                                        if (!Settings.Instance.NestedAsText)
                                        {
                                            skip = true;
                                            _postProcessing.Enqueue(currentNode);
                                        }
                                        break;
                                    case "today":
                                        shape.TextFrame.TextRange.InsertDateTime(PowerPoint.PpDateTimeFormat.ppDateTimeFigureOut, MsoTriState.msoTrue);
                                        break;
                                    default: // other -> check for simple formats
                                        SimpleTextFormat(nodes, currentNode);
                                        break;
                                }

                                if (currentNode.Children == null || skip)
                                    continue;

                                // push child nodes to stack
                                foreach (Node item in currentNode.Children.Reverse<Node>())
                                {
                                    nodes.Push(item);
                                }
                            }

                            if (rowcontent.Type == "tablecolumn")
                            {
                                if (columnCounter == 1 && settings.Borders.Contains(0)) // first column check also for border with index 0 (left border)
                                {
                                    tableShape.Table.Rows[rowCounter].Cells[columnCounter].Borders[PowerPoint.PpBorderType.ppBorderLeft].ForeColor.RGB = 0x0;
                                    tableShape.Table.Rows[rowCounter].Cells[columnCounter].Borders[PowerPoint.PpBorderType.ppBorderLeft].DashStyle = MsoLineDashStyle.msoLineSolid;
                                }

                                if (settings.Borders.Contains(columnCounter))   // for every column set right border
                                {
                                    tableShape.Table.Rows[rowCounter].Cells[columnCounter].Borders[PowerPoint.PpBorderType.ppBorderRight].ForeColor.RGB = 0x0;
                                    tableShape.Table.Rows[rowCounter].Cells[columnCounter].Borders[PowerPoint.PpBorderType.ppBorderRight].DashStyle = MsoLineDashStyle.msoLineSolid;
                                }
                            }

                            // merge cells
                            if (rowcontent.Type == "tablecolumn_merged")
                            {
                                // merge cells here and increment columnCounter depending on number of merged cells
                                string tmp = rowcontent.Content as string;

                                int merge_count;

                                if (int.TryParse(tmp.Trim(), out merge_count))
                                {
                                    // merge cells
                                    if(merge_count > 1)
                                        tableShape.Table.Cell(rowCounter, columnCounter).Merge(tableShape.Table.Cell(rowCounter, columnCounter + merge_count - 1));

                                    TabularSettings mset = TabularSettings.Parse(rowcontent.OptionalParams, true);

                                    // left border
                                    if (mset.Borders.Contains(0))
                                    {
                                        tableShape.Table.Cell(rowCounter, columnCounter).Borders[PowerPoint.PpBorderType.ppBorderLeft].ForeColor.RGB = 0x0;
                                        tableShape.Table.Cell(rowCounter, columnCounter).Borders[PowerPoint.PpBorderType.ppBorderLeft].DashStyle = MsoLineDashStyle.msoLineSolid;
                                    }

                                    // right border
                                    if (mset.Borders.Contains(1))
                                    {
                                        tableShape.Table.Cell(rowCounter, columnCounter + merge_count - 1).Borders[PowerPoint.PpBorderType.ppBorderRight].ForeColor.RGB = 0x0;
                                        tableShape.Table.Cell(rowCounter, columnCounter + merge_count - 1).Borders[PowerPoint.PpBorderType.ppBorderRight].DashStyle = MsoLineDashStyle.msoLineSolid;
                                    }

                                    // set cell alignment
                                    switch (mset.Columns[0].alignment)
                                    {
                                        case 'l':
                                            shape.TextFrame2.TextRange.ParagraphFormat.Alignment = MsoParagraphAlignment.msoAlignLeft;
                                            break;
                                        case 'c':
                                            shape.TextFrame2.TextRange.ParagraphFormat.Alignment = MsoParagraphAlignment.msoAlignCenter;
                                            break;
                                        case 'r':
                                            shape.TextFrame2.TextRange.ParagraphFormat.Alignment = MsoParagraphAlignment.msoAlignRight;
                                            break;
                                        case 'p':
                                            shape.TextFrame2.TextRange.ParagraphFormat.Alignment = MsoParagraphAlignment.msoAlignJustify;
                                            break;
                                        default:
                                            break;
                                    }

                                    // skip merged columns
                                    columnCounter += merge_count - 1;
                                }
                            }
                        }
                    }
                }
                else if(node.Type == "hline")
                {
                    if (rowCounter == 0)
                    {
                        tableShape.Table.Rows[1].Cells.Borders[PowerPoint.PpBorderType.ppBorderTop].ForeColor.RGB = 0x0;
                        tableShape.Table.Rows[1].Cells.Borders[PowerPoint.PpBorderType.ppBorderTop].DashStyle = MsoLineDashStyle.msoLineSolid;
                    }
                    else
                    {
                        tableShape.Table.Rows[rowCounter].Cells.Borders[PowerPoint.PpBorderType.ppBorderBottom].ForeColor.RGB = 0x0;
                        tableShape.Table.Rows[rowCounter].Cells.Borders[PowerPoint.PpBorderType.ppBorderBottom].DashStyle = MsoLineDashStyle.msoLineSolid;
                    }
                }
                else if (node.Type == "cline")
                {
                    Regex regex = new Regex(@"^([0-9]+)-([0-9]+)$", RegexOptions.IgnoreCase);

                    string range = node.Content as string;

                    Match match = regex.Match(range.Trim());

                    if (match.Success)
                    {
                        int x, y;

                        if (int.TryParse(match.Groups[1].Value, out x) && int.TryParse(match.Groups[2].Value, out y))
                        {
                            for (int i = Math.Min(x,y); i <= Math.Max(x,y); i++)
                            {
                                if (rowCounter == 0)
                                {
                                    tableShape.Table.Rows[1].Cells[i].Borders[PowerPoint.PpBorderType.ppBorderTop].ForeColor.RGB = 0x0;
                                    tableShape.Table.Rows[1].Cells[i].Borders[PowerPoint.PpBorderType.ppBorderTop].DashStyle = MsoLineDashStyle.msoLineSolid;
                                }
                                else
                                {
                                    tableShape.Table.Rows[rowCounter].Cells[i].Borders[PowerPoint.PpBorderType.ppBorderBottom].ForeColor.RGB = 0x0;
                                    tableShape.Table.Rows[rowCounter].Cells[i].Borders[PowerPoint.PpBorderType.ppBorderBottom].DashStyle = MsoLineDashStyle.msoLineSolid;
                                }
                            }
                        }
                    }
                }
            }

            // resize table
            Misc.AutoFitColumn(tableShape, settings);

            // if processing was paused remove all lines after pause commands (columns are not supported yet)
            if (paused)
            {
                if (pausedAfter == 0)
                {
                    tableShape.Delete();
                    tableShape = null;
                }
                else
                {
                    for (int i = tableShape.Table.Rows.Count; i > pausedAfter; i--)
                    {
                        tableShape.Table.Rows[i].Delete();
                    }
                }

                return false;
            }

            return true;
        }
        /// <summary>
        /// Insert image from its node
        /// </summary>
        /// <param name="imageNode">Image node</param>
        /// <param name="imageShape">output - Shape of inserted image (used for reshaper), null if no image was inserted</param>
        private void GenerateImage(Node imageNode, out PowerPoint.Shape imageShape)
        {
            imageShape = null;

            // find inserted image
            string imagePath = Misc.FindImage(imageNode.Content as string, _preambuleSettings.GraphicsPath);
            if (imagePath == null)
            {
                throw new DocumentBuilderException("Image '" + imageNode.Content + "' not found!");
            }

            try     // just to be sure
            {
                imageShape = _slide.Shapes.AddPicture(imagePath, MsoTriState.msoFalse, MsoTriState.msoTrue, 36.0f, _bottomShapeBorder + 5.0f);
            }
            catch (Exception)
            {
                // throw image not found exception
                throw new DocumentBuilderException("Image '" + imageNode.Content + "' not found!");
            }

            float width = 0, height = 0, scale = 0;

            // parse optional parameters
            string[] optParams = imageNode.OptionalParams.Replace(" ", "").Split(new Char[] {','}, StringSplitOptions.RemoveEmptyEntries);
            foreach (string item in optParams)
            {
                string[] pair = item.Split('=');

                switch (pair[0])
                {
                    case "width":
                        if (pair.Length > 1)
                            width = Misc.ParseLength(pair[1]);
                        break;
                    case "height":
                        if (pair.Length > 1)
                            height = Misc.ParseLength(pair[1]);
                        break;
                    case "scale":
                        if (pair.Length > 1)
                            float.TryParse(pair[1], out scale);
                        break;
                    default:
                        break;
                }
            }

            // resize image according to optinal params

            if (width != 0 && height != 0)  // width and height was set in optinal parameters
            {
                imageShape.LockAspectRatio = MsoTriState.msoFalse;
                imageShape.Height = height;
                imageShape.Width = width;
            }
            else if (width != 0)  // only width was set
            {
                imageShape.LockAspectRatio = MsoTriState.msoTrue;
                imageShape.Width = width;
            }
            else if (height != 0)   // only height was set
            {
                imageShape.LockAspectRatio = MsoTriState.msoTrue;
                imageShape.Height = height;
            }
            else if (scale != 0) // scale was set
            {
                imageShape.LockAspectRatio = MsoTriState.msoTrue;
                imageShape.Width *= scale;
            }
        }
        /// <summary>
        /// Process simple text formatting
        /// </summary>
        /// <param name="nodes">Nodes stack - used for pushing format rollback node</param>
        /// <param name="node">Current node</param>
        private void SimpleTextFormat(Stack<Node> nodes, Node node)
        {
            Node rollbackNode = new Node("__format_pop");

            switch (node.Type)
            {
                case "bold":
                case "italic":
                case "underline":
                case "smallcaps":
                case "typewriter":
                case "color":
                case "tiny":
                case "scriptsize":
                case "footnotesize":
                case "small":
                case "normalsize":
                case "large":
                case "Large":
                case "LARGE":
                case "huge":
                case "Huge":
                    // check overlay settings
                    if (Misc.ShowOverlay(_passNumber, node.OverlayList, ref _maxPass))
                    {
                        _format.ModifyFormat(node);
                        nodes.Push(rollbackNode);
                    }
                    break;
                case "__format_pop":    // special node -> pop formatting from stack
                    _format.RollBackFormat();
                    break;
                default:    // unknown node -> ignore
                    break;
            }
        }
        /// <summary>
        /// Build slide content
        /// </summary>
        /// <param name="slide">Slide in PowerPoint presentation</param>
        /// <param name="slideNode">Node containing content of slide</param>
        /// <param name="passNumber">Number of current pass (used for overlays)</param>
        /// <param name="pauseCounter">Number of used pauses</param>
        /// <param name="paused">output - processing of slide content was paused</param>
        /// <returns>true if slide is complete; false if needs another pass</returns>
        public bool BuildSlide(PowerPoint.Slide slide, Node slideNode, int passNumber, int pauseCounter, out bool paused)
        {
            _slide = slide;
            _passNumber = passNumber;
            _pauseCounter = pauseCounter;
            // copy title settings
            _titlesettings = new Dictionary<string,List<Node>>(_preambuleSettings.TitlepageSettings);
            _format = new TextFormat(_baseFontSize);
            _postProcessing = new Queue<Node>();

            if (!_called)
            {
                // because title is processed before slide and can contain pause command, we need to setup internal counters according to title passed at first processing
                _localPauseCounter = pauseCounter;
                _localPauseCounterStart = pauseCounter;
                _maxPass = passNumber;
                _called = true;
            }
            else
            {
                _localPauseCounter = _localPauseCounterStart;
            }

            _bottomShapeBorder = 0.0f;

            UpdateBottomShapeBorder();

            paused = !ProcessSlideContent(slideNode);

            if (!Settings.Instance.NestedAsText)    // extract nested elements
            {
                _localPauseCounter = int.MinValue;  // ignore pauses in post processed shapes
                PostProcessing();
            }

            return _passNumber >= _maxPass;
        }
        /// <summary>
        /// Process slide content
        /// </summary>
        /// <param name="slideNode">Slide content node</param>
        /// <returns>true if completed; false if paused</returns>
        private bool ProcessSlideContent(Node slideNode)
        {
            // note: width of slide content area is 648.0
            if (slideNode.Children.Count == 0)   // ignore empty node
                return true;

            Stack<Node> nodes = new Stack<Node>();

            // copy content to stack
            foreach (Node item in slideNode.Children.Reverse<Node>())
            {
                nodes.Push(item);
            }

            Node currentNode;

            PowerPoint.Shape shape = null;

            // skip expanding of child nodes to stack
            bool skip;

            // list of shapes used for reshaper
            List<PowerPoint.Shape> reshapeShapes = new List<PowerPoint.Shape>();

            // process nodes on stack
            while (nodes.Count != 0)
            {
                currentNode = nodes.Pop();
                skip = false;

                if(reshapeShapes.Count > 1)
                    Reshaper(reshapeShapes);

                // process node depending on its type
                switch (currentNode.Type)
                {
                    case "string":
                        if (shape == null)
                        {
                            UpdateBottomShapeBorder(true);
                            _format.Invalidate();
                            shape = _slide.Shapes.AddTextbox(MsoTextOrientation.msoTextOrientationHorizontal, 36.0f, _bottomShapeBorder + 5.0f, 648.0f, 10.0f);
                            string tmp = currentNode.Content as string;
                            if (Misc.StartsWithNewLine(tmp))    // if new shape starts with new line, then call reshaper on previous shapes
                            {
                                Reshaper(reshapeShapes);
                                reshapeShapes.Clear();
                                tmp = tmp.TrimStart();
                                if (tmp.Length == 0)    // only empty paragraph -> don't start new shape
                                {
                                    shape.Delete();
                                    shape = null;
                                }
                                else
                                {
                                    _format.AppendText(shape, tmp);
                                }
                            }
                            else
                            {
                                _format.AppendText(shape, tmp);
                            }
                            reshapeShapes.Add(shape);
                        }
                        else
                        {
                            _format.AppendText(shape, currentNode.Content as string);
                        }
                        break;
                    case "paragraph":
                        if(shape != null && shape.HasTextFrame == MsoTriState.msoTrue && !Misc.EndsWithNewLine(shape.TextFrame2.TextRange.Text))
                            _format.AppendText(shape, "\r");

                        // also do reshaping
                        Reshaper(reshapeShapes);
                        reshapeShapes.Clear();

                        break;
                    case "pause":
                        if (Pause())
                            return false;
                        break;
                    case "today":
                        if (shape == null)
                        {
                            UpdateBottomShapeBorder(true);
                            _format.Invalidate();
                            shape = _slide.Shapes.AddTextbox(MsoTextOrientation.msoTextOrientationHorizontal, 36.0f, _bottomShapeBorder + 5.0f, 648.0f, 10.0f);
                            reshapeShapes.Add(shape);
                        }
                        shape.TextFrame.TextRange.InsertDateTime(PowerPoint.PpDateTimeFormat.ppDateTimeFigureOut, MsoTriState.msoTrue);
                        break;
                    case "image":
                        // previous shape ends with new paragraph - image will start on new line
                        if (shape != null && shape.HasTextFrame == MsoTriState.msoTrue && Misc.EndsWithNewLine(shape.TextFrame2.TextRange.Text))
                        {
                            Reshaper(reshapeShapes);
                            reshapeShapes.Clear();
                        }
                        UpdateBottomShapeBorder(true);
                        PowerPoint.Shape imageShape;

                        GenerateImage(currentNode, out imageShape);
                        reshapeShapes.Add(imageShape);

                        shape = null;
                        break;
                    case "bulletlist":
                    case "numberedlist":
                        skip = true;

                        Reshaper(reshapeShapes);
                        reshapeShapes.Clear();

                        UpdateBottomShapeBorder(true);
                        shape = _slide.Shapes.AddTextbox(MsoTextOrientation.msoTextOrientationHorizontal, 36.0f, _bottomShapeBorder + 5.0f, 648.0f, 10.0f);

                        if (!GenerateList(currentNode.Children, shape, 1, currentNode.Type == "bulletlist" ? MsoBulletType.msoBulletUnnumbered : MsoBulletType.msoBulletNumbered))
                            return false;

                        shape = null;
                        break;
                    case "table":
                        skip = true;
                        // previous shape ends with new paragraph - table will start on new line
                        if (shape != null && shape.HasTextFrame == MsoTriState.msoTrue && Misc.EndsWithNewLine(shape.TextFrame2.TextRange.Text))
                        {
                            Reshaper(reshapeShapes);
                            reshapeShapes.Clear();
                        }
                        UpdateBottomShapeBorder(true);
                        PowerPoint.Shape tableShape;

                        if (!GenerateTable(currentNode, out tableShape))
                        {
                            reshapeShapes.Add(tableShape);  // reshape before ending this pass
                            Reshaper(reshapeShapes);

                            return false;   // table processing was paused
                        }

                        if (tableShape == null) // table wasn't generated so call reshaper and next shape will start at new "line"
                        {
                            Reshaper(reshapeShapes);
                            reshapeShapes.Clear();
                        }
                        else
                        {
                            reshapeShapes.Add(tableShape);
                        }

                        shape = null;
                        break;
                    case "descriptionlist":
                        skip = true;

                        Reshaper(reshapeShapes);
                        reshapeShapes.Clear();

                        UpdateBottomShapeBorder(true);

                        if (!GenerateDescriptionList(currentNode.Children))
                            return false;

                        shape = null;
                        break;
                    case "titlepage":

                        Reshaper(reshapeShapes);
                        reshapeShapes.Clear();

                        UpdateBottomShapeBorder(true);

                        GenerateTitlePage();

                        shape = null;
                        break;
                    default: // other -> check for simple formats
                        SimpleTextFormat(nodes, currentNode);
                        break;
                }

                if (currentNode.Children == null || skip)
                    continue;

                // push child nodes to stack
                foreach (Node item in currentNode.Children.Reverse<Node>())
                {
                    nodes.Push(item);
                }
            }

            // final reshaper call on slide
            Reshaper(reshapeShapes);

            return true;
        }
Example #7
0
        /// <summary>
        /// Modify current formatting according to node.
        /// </summary>
        /// <param name="node">Node containing new font settings</param>
        public void ModifyFormat(Node node)
        {
            // save current font settings
            _settingsStack.Push(_currentSettings.Clone() as FormatSettings);

            _changed = true;

            switch (node.Type)
            {
                case "bold":
                    _currentSettings.Bold = MsoTriState.msoTrue;
                    break;
                case "italic":
                    _currentSettings.Italic = MsoTriState.msoTrue;
                    break;
                case "underline":
                    _currentSettings.Underline = MsoTextUnderlineType.msoUnderlineSingleLine;
                    break;
                case "smallcaps":
                    _currentSettings.Smallcaps = MsoTriState.msoTrue;
                    break;
                case "typewriter":
                    _currentSettings.FontFamily = @"Courier New";
                    break;
                case "color":
                    _currentSettings.Color = ParseColor(node.OptionalParams, node.Content as string);
                    break;
                // coeficients for relative font size are computed from default font size for each LaTeX size command
                case "tiny":
                    _currentSettings.FontSize = _baseFontSize / 2f;
                    break;
                case "scriptsize":
                    _currentSettings.FontSize = _baseFontSize / 1.4285714285714285714285714285714f;
                    break;
                case "footnotesize":
                    _currentSettings.FontSize = _baseFontSize / 1.25f;
                    break;
                case "small":
                    _currentSettings.FontSize = _baseFontSize / 1.1111111111111111111111111111111f;
                    break;
                case "normalsize":
                    _currentSettings.FontSize = _baseFontSize;
                    break;
                case "large":
                    _currentSettings.FontSize = _baseFontSize * 1.2f;
                    break;
                case "Large":
                    _currentSettings.FontSize = _baseFontSize * 1.44f;
                    break;
                case "LARGE":
                    _currentSettings.FontSize = _baseFontSize * 1.728f;
                    break;
                case "huge":
                    _currentSettings.FontSize = _baseFontSize * 2.0736f;
                    break;
                case "Huge":
                    _currentSettings.FontSize = _baseFontSize * 2.48832f;
                    break;
                default:
                    _settingsStack.Pop();   // no changes -> throw away stack top (saved at start of this method)
                    _changed = false;
                    break;
            }
        }
        /// <summary>
        /// Build (generate) title part.
        /// </summary>
        /// <param name="shape">Prepared title shape</param>
        /// <param name="content">Title content</param>
        /// <param name="format">Text format</param>
        /// <returns>true if completed; false if paused</returns>
        private bool BuildTitlePart(PowerPoint.Shape shape, List<Node> content, TextFormat format)
        {
            if (content.Count == 0)   // ignore empty node
                return true;

            Stack<Node> nodes = new Stack<Node>();

            // copy content to stack
            foreach (Node item in content.Reverse<Node>())
            {
                nodes.Push(item);
            }

            Node currentNode;
            Node rollbackNode = new Node("__format_pop");

            // process nodes on stack
            while (nodes.Count != 0)
            {
                currentNode = nodes.Pop();

                // process node depending on its type
                switch (currentNode.Type)
                {
                    case "bold":
                    case "italic":
                    case "underline":
                    case "smallcaps":
                    case "typewriter":
                    case "color":
                    case "tiny":
                    case "scriptsize":
                    case "footnotesize":
                    case "small":
                    case "normalsize":
                    case "large":
                    case "Large":
                    case "LARGE":
                    case "huge":
                    case "Huge":
                        // check overlay settings
                        int min = currentNode.OverlayList.Count != 0 ? currentNode.OverlayList.Min() : int.MaxValue;
                        _maxPass = Math.Max(Misc.MaxOverlay(currentNode.OverlayList), _maxPass);    // set maximal number of passes from overlay specification
                        if (currentNode.OverlayList.Count == 0 || currentNode.OverlayList.Contains(_passNumber) || min < 0 && Math.Abs(min) < _passNumber)
                        {
                            format.ModifyFormat(currentNode);
                            nodes.Push(rollbackNode);
                        }
                        break;
                    case "__format_pop":    // special node -> pop formatting from stack
                        format.RollBackFormat();
                        break;
                    case "string":
                        format.AppendText(shape, currentNode.Content as string);
                        break;
                    case "paragraph":
                        format.AppendText(shape, "\r");
                        break;
                    case "today":
                        shape.TextFrame.TextRange.InsertDateTime(PowerPoint.PpDateTimeFormat.ppDateTimeFigureOut, MsoTriState.msoTrue);
                        break;
                    case "pause":
                        _localPauseCounter++;

                        if (_localPauseCounter > _pauseCounter)
                        {
                            if (_passNumber == _maxPass)    // increase number of passes
                                _maxPass++;

                            return false;
                        }
                        break;
                    default: // unknown node -> ignore
                        break;
                }

                if (currentNode.Children == null)
                    continue;

                // push child nodes to stack
                foreach (Node item in currentNode.Children.Reverse<Node>())
                {
                    nodes.Push(item);
                }
            }

            return true;
        }
        /// <summary>
        /// Process slide content
        /// </summary>
        /// <param name="slideNode">Slide node</param>
        private void ProcessSlide(Node slideNode)
        {
            // presentation slide
            PowerPoint.Slide slide;

            // increment current slide number
            _currentSlide++;

            int passNumber = 0,
                pauseCounter = 0;

            int maxPass = 0;

            bool titleNextPass = true,
                slideNextPass = true,
                paused = false;

            SlideBuilder slideBuilder = new SlideBuilder(_preambuleSettings, _currentSlide, _baseFontSize);
            TitleBuilder titleBuilder = new TitleBuilder(_baseFontSize);

            // list of sub-slides
            List<PowerPoint.Slide> subSlides = new List<PowerPoint.Slide>();

            // slide settings
            SlideSettings slideSettings = new SlideSettings(slideNode.OptionalParams);

            do
            {    // --- loop over all overlays

                if (paused)
                {
                    pauseCounter++;
                    paused = false;
                }

                passNumber++;
                _slideIndex++;

                // create new slide -> if slide contains title, use layout with title
                if (_frametitleTable.ContainsKey(_currentSlide))
                {
                    FrametitleRecord record = _frametitleTable[_currentSlide];

                    // check if slide has title for current pass
                    if (record.SubtitleOverlaySet.Count == 0 && record.Subtitle != null
                        || record.TitleOverlaySet.Count == 0 && record.Title != null
                        || Misc.ShowOverlay(passNumber, record.SubtitleOverlaySet, ref maxPass) && record.Subtitle != null
                        || Misc.ShowOverlay(passNumber, record.TitleOverlaySet, ref maxPass) && record.Title != null)
                    {
                        slide = _pptPresentation.Slides.Add(_slideIndex, PowerPoint.PpSlideLayout.ppLayoutTitleOnly);
                        subSlides.Add(slide);   // add slide to list of sub-slides

                        titleNextPass = titleBuilder.BuildTitle(slide.Shapes.Title, _frametitleTable[_currentSlide], passNumber, pauseCounter, out paused);

                        if (paused)
                            continue;

                        slideNextPass = slideBuilder.BuildSlide(slide, slideNode, passNumber, pauseCounter, out paused);

                        continue;
                    }
                }

                slide = _pptPresentation.Slides.Add(_slideIndex, PowerPoint.PpSlideLayout.ppLayoutBlank);
                subSlides.Add(slide);   // add slide to list of sub-slides

                slideNextPass = slideBuilder.BuildSlide(slide, slideNode, passNumber, pauseCounter, out paused);

            } while (!titleNextPass || !slideNextPass); // --- end loop over all overlays

            // change slide content vertical align
            if (slideSettings.ContentAlign != SlideSettings.Align.TOP)
            {
                float bottom = float.MinValue;

                // compute maximal top and bottom of slide content
                foreach (PowerPoint.Slide subSlide in subSlides)
                {
                    bottom = Math.Max(bottom, subSlide.Shapes[subSlide.Shapes.Count].Top + subSlide.Shapes[subSlide.Shapes.Count].Height);
                }

                float change;

                if (slideSettings.ContentAlign == SlideSettings.Align.BOTTOM)
                    change = (540.0f - bottom) - 10.0f;
                else
                    change = (540.0f - bottom) / 2.5f;

                foreach (PowerPoint.Slide subSlide in subSlides)
                {
                    foreach (PowerPoint.Shape shape in subSlide.Shapes)
                    {
                        if (subSlide.Shapes.HasTitle != MsoTriState.msoTrue || shape != subSlide.Shapes.Title)
                        {
                            shape.Top = shape.Top + change;
                        }
                    }
                }
            }

            // report progress
            RaiseProgress();
        }
        /// <summary>
        /// Process document preambule part
        /// </summary>
        /// <param name="preambule">Preambule node</param>
        /// <param name="documentclassOptionals">\document class optional parameters (size)</param>
        private void ProcessPreambule(Node preambule, string documentclassOptionals)
        {
            // parse preambule
            _preambuleSettings.Parse(preambule);

            // process \documentclass optional parameters
            string[] parts = documentclassOptionals.Split(new char[]{','}, StringSplitOptions.RemoveEmptyEntries);

            foreach (string part in parts)
            {
                float size = Misc.ParseLength(part);
                if (size > 0)
                    _baseFontSize = Settings.Instance.AdjustSize ? size / 2 : size;
            }
        }
 /// <summary>
 /// Process document body part
 /// </summary>
 /// <param name="body">Body node</param>
 private void ProcessBody(Node body)
 {
     foreach (Node node in body.Children)
     {
         switch (node.Type)
         {
             // title page settings
             case "author":
             case "title":
             case "institute":
             case "date":
                 _preambuleSettings.TitlepageSettings[node.Type] = node.Children;
                 break;
             // slide
             case "slide":
                 ProcessSlide(node);
                 break;
             // unknown or invalid node -> ignore
             default:
                 break;
         }
     }
 }
        /// <summary>
        /// Build presentation from document tree.
        /// </summary>
        /// <param name="filename">Filename of input file (used for output file)</param>
        /// <param name="outputPath">Output directory</param>
        /// <param name="document">Document tree</param>
        /// <param name="slideCount">Number of slides in document tree</param>
        /// <param name="sectionTable">Table of document sections</param>
        /// <param name="frametitleTable">Table of frame titles</param>
        public void Build(string filename, string outputPath, Node document, int slideCount, List<SectionRecord> sectionTable, Dictionary<int, FrametitleRecord> frametitleTable)
        {
            #region Initialize internal variables
            if (outputPath.Length == 0)
            {
                _filename = Path.Combine(Directory.GetCurrentDirectory(), "output", Path.GetFileNameWithoutExtension(filename));

                try
                {
                    // if output directory doesn't exist then create it
                    if (!Directory.Exists(Path.Combine(Directory.GetCurrentDirectory(), "output")))
                        Directory.CreateDirectory(Path.Combine(Directory.GetCurrentDirectory(), "output"));
                }
                catch (Exception)
                {
                    throw new PowerPointApplicationException("Couldn't create default output directory.");
                }
            }
            else
            {
                _filename = Path.Combine(outputPath, Path.GetFileNameWithoutExtension(filename));

                try
                {
                    // if output directory doesn't exist then create it
                    if (!Directory.Exists(outputPath))
                        Directory.CreateDirectory(outputPath);
                }
                catch (Exception)
                {
                    throw new PowerPointApplicationException("Couldn't create output directory.");
                }
            }

            _document = document;
            _slideCount = slideCount;

            _sectionTable = sectionTable ?? new List<SectionRecord>();
            _frametitleTable = frametitleTable ?? new Dictionary<int, FrametitleRecord>();

            _preambuleSettings = new PreambuleSettings(Path.GetDirectoryName(filename));

            _currentSlide = 0;
            _slideIndex = 0;

            _currentProgress = BasicProgress;

            #endregion // Initialize internal variables

            Node preambule = _document.FindFirstNode("preambule");
            if (preambule == null)
                throw new DocumentBuilderException("Couldn't build document, something went wrong. Please try again.");

            ProcessPreambule(preambule, _document.OptionalParams);

            // create new presentation without window
            _pptPresentation = _pptApplication.Presentations.Add(MsoTriState.msoFalse);

            Node body = _document.FindFirstNode("body");
            if (body == null)
                throw new DocumentBuilderException("Couldn't build document, something went wrong. Please try again.");

            ProcessBody(body);

            try
            {
                _pptPresentation.SaveAs(_filename, Settings.Instance.SaveAs);
            }
            catch (Exception)
            {
                throw new DocumentBuilderException("Couldn't save output file.");
            }
            finally
            {
                // final progress change after saving
                RaiseProgress();
            }

            // print save message
            switch (Settings.Instance.SaveAs)
            {
                case Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType.ppSaveAsDefault:
                case Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType.ppSaveAsOpenXMLPresentation:
                case Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType.ppSaveAsPresentation:
                    Messenger.Instance.SendMessage(System.IO.Path.GetFileName(filename) + " - Output saved to: \"" + _pptPresentation.FullName + "\"");
                    break;
                case Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType.ppSaveAsPDF:
                    Messenger.Instance.SendMessage(System.IO.Path.GetFileName(filename) + " - Output saved to: \"" + _filename + ".pdf\"");
                    break;
                default:
                    Messenger.Instance.SendMessage(System.IO.Path.GetFileName(filename) + " - Output saved to output directory.");
                    break;
            }

            try
            {
                _pptPresentation.Close();
                _pptPresentation = null;
            }
            catch { }
        }