/// <summary> /// Copies any missing source formatting over to the destination. /// </summary> private MarkupRange FixupDestinationFormatting() { // We'll use the concept of a "fixup segment" to describe a destination segment that needs to be // reformatted to match its corresponding source segment. This is very similar to what MSHTML does // internally. var openFixupSegments = new Dictionary<Type, FixupSegment>(); var registeredFixupSegments = new List<FixupSegment>(); // We'll use these to track our current range as we scan through the source and destination ranges. MarkupRange currentSource = sourceMarkupServices.CreateMarkupRange(sourceRange.Start.Clone(), sourceRange.Start.Clone()); MarkupRange currentDestination = destinationMarkupServices.CreateMarkupRange(destinationRange.Start.Clone(), destinationRange.Start.Clone()); MarkupContext sourceContext = MoveNext(currentSource); MarkupContext destinationContext = MoveNext(currentDestination); // Loop through the source and destination at the same time, scanning to the right. while (currentSource.End.IsLeftOfOrEqualTo(sourceRange.End)) { Trace.Assert(sourceContext.Context == destinationContext.Context, "Mismatched contexts!"); Trace.Assert((sourceContext.Element == null && destinationContext.Element == null) || (sourceContext.Element != null && destinationContext.Element != null && String.Compare(sourceContext.Element.tagName, destinationContext.Element.tagName, StringComparison.OrdinalIgnoreCase) == 0), "Mismatched tags!"); // If it is an image, add attribute marker to suppress applying default values for image decorators if (sourceContext.Element != null && destinationContext.Element != null && string.Compare(sourceContext.Element.tagName, "IMG", StringComparison.OrdinalIgnoreCase) == 0) { destinationContext.Element.setAttribute("wlNoDefaultDecorator", "true", 0); } if (IsBeginTag(sourceContext)) { // Fix up all but text-related formatting. CopyMinimumCss(sourceContext.Element, destinationContext.Element); RemoveTextRelatedInlineCss(destinationContext.Element); MoveCssPropertiesToHtmlAttributes(sourceContext.Element, destinationContext.Element); } if (IsText(sourceContext)) { // Fix up text-related formatting. TextStyles sourceStyles = new TextStyles(currentSource.Start); TextStyles destinationStyles = new TextStyles(currentDestination.Start); IEnumerator<TextStyle> sourceEnumerator = sourceStyles.GetEnumerator(); IEnumerator<TextStyle> destinationEnumerator = destinationStyles.GetEnumerator(); while (sourceEnumerator.MoveNext() && destinationEnumerator.MoveNext()) { TextStyle sourceStyle = sourceEnumerator.Current; TextStyle destinationStyle = destinationEnumerator.Current; FixupSegment fixupSegment; if (openFixupSegments.TryGetValue(destinationStyle.GetType(), out fixupSegment)) { // We've moved into a new range, so we may want to end an open fixup segment. if (ShouldEndFixupSegment(sourceStyle, destinationStyle, fixupSegment)) { registeredFixupSegments.Add(fixupSegment); openFixupSegments.Remove(destinationStyle.GetType()); fixupSegment = null; } } // We've moved into a new range, so we may want to start a new fixup segment. if (ShouldStartFixupSegment(sourceStyle, destinationStyle, fixupSegment)) { openFixupSegments[destinationStyle.GetType()] = new FixupSegment(currentDestination, sourceStyle); } else if (fixupSegment != null) { // There's an open fixup segment for this style and we don't want to start a new one. // That must mean we want to extend the current one. fixupSegment.RangeToFixup.End.MoveToPointer(currentDestination.End); } } } else if (IsEndTag(sourceContext) && !IsInlineElement(sourceContext.Element)) { // We're moving out of a block element and <font> tags cannot wrap block elements so we need to // end any open fixup segments. EndOpenFixupSegments(openFixupSegments, registeredFixupSegments); } // Move along. sourceContext = MoveNext(currentSource); destinationContext = MoveNext(currentDestination); } EndOpenFixupSegments(openFixupSegments, registeredFixupSegments); ExecuteRegisteredFixupSegments(registeredFixupSegments); return destinationRange; }