} // IRtfElementVisitor.VisitTag void IRtfElementVisitor.VisitGroup(IRtfGroup group) { var groupDestination = group.Destination; switch (Context.State) { case RtfInterpreterState.Init: if (RtfSpec.TagRtf.Equals(groupDestination)) { VisitChildrenOf(group); } else { throw new RtfStructureException(Strings.InvalidInitGroupState(groupDestination)); } break; case RtfInterpreterState.InHeader: switch (groupDestination) { case RtfSpec.TagFontTable: _fontTableBuilder.VisitGroup(group); break; case RtfSpec.TagColorTable: _colorTableBuilder.VisitGroup(group); break; case RtfSpec.TagGenerator: // last group with a destination in header, but no need to process its contents Context.State = RtfInterpreterState.InDocument; var generator = group.Contents.Count == 3 ? group.Contents[2] as IRtfText : null; if (generator != null) { var generatorName = generator.Text; Context.Generator = generatorName.EndsWith(";") ? generatorName.Substring(0, generatorName.Length - 1) : generatorName; } else { throw new RtfInvalidDataException(Strings.InvalidGeneratorGroup(group.ToString())); } break; case RtfSpec.TagPlain: case RtfSpec.TagParagraphDefaults: case RtfSpec.TagSectionDefaults: case RtfSpec.TagUnderLineNone: case null: // <tags>: special tags commonly used to reset state in a beginning group. necessary to recognize // state transition form header to document in case no other mechanism detects it and the // content starts with a group with such a 'destination' ... // 'null': group without destination cannot be part of header, but need to process its contents Context.State = RtfInterpreterState.InDocument; if (!group.IsExtensionDestination) { VisitChildrenOf(group); } break; } break; case RtfInterpreterState.InDocument: switch (groupDestination) { case RtfSpec.TagUserProperties: _userPropertyBuilder.VisitGroup(group); break; case RtfSpec.TagInfo: _documentInfoBuilder.VisitGroup(group); break; case RtfSpec.TagUnicodeAlternativeChoices: var alternativeWithUnicodeSupport = group.SelectChildGroupWithDestination(RtfSpec.TagUnicodeAlternativeUnicode); if (alternativeWithUnicodeSupport != null) { // there is an alternative with unicode formatted content -> use this VisitChildrenOf(alternativeWithUnicodeSupport); } else { // try to locate the alternative without unicode -> only ANSI fallbacks var alternativeWithoutUnicode = // must be the third element if present group.Contents.Count > 2 ? group.Contents[2] as IRtfGroup : null; if (alternativeWithoutUnicode != null) { VisitChildrenOf(alternativeWithoutUnicode); } } break; case RtfSpec.TagHeader: case RtfSpec.TagHeaderFirst: case RtfSpec.TagHeaderLeft: case RtfSpec.TagHeaderRight: case RtfSpec.TagFooter: case RtfSpec.TagFooterFirst: case RtfSpec.TagFooterLeft: case RtfSpec.TagFooterRight: case RtfSpec.TagFootnote: case RtfSpec.TagStyleSheet: // groups we currently ignore, so their content doesn't intermix with // the actual document content break; case RtfSpec.TagPictureWrapper: VisitChildrenOf(group); _lastGroupWasPictureWrapper = true; break; case RtfSpec.TagPictureWrapperAlternative: if (!_lastGroupWasPictureWrapper) { VisitChildrenOf(group); } _lastGroupWasPictureWrapper = false; break; case RtfSpec.TagPicture: _imageBuilder.VisitGroup(group); NotifyInsertImage( _imageBuilder.Format, _imageBuilder.Width, _imageBuilder.Height, _imageBuilder.DesiredWidth, _imageBuilder.DesiredHeight, _imageBuilder.ScaleWidthPercent, _imageBuilder.ScaleHeightPercent, _imageBuilder.ImageDataHex); break; case RtfSpec.TagParagraphNumberText: case RtfSpec.TagListNumberText: NotifyInsertSpecialChar(RtfVisualSpecialCharKind.ParagraphNumberBegin); VisitChildrenOf(group); NotifyInsertSpecialChar(RtfVisualSpecialCharKind.ParagraphNumberEnd); break; default: if (!group.IsExtensionDestination) { VisitChildrenOf(group); } break; } break; } } // IRtfElementVisitor.VisitGroup