protected bool ProcessParagraphs(OfficeDocumentProcessor aWordProcessor, Word.Paragraphs aParagraphs) { foreach (Word.Paragraph aParagraph in aParagraphs) { // get the Range object for this paragraph Word.Range aParagraphRange = aParagraph.Range; // if we're picking up where we left off and we're not there yet... int nCharIndex = 0; if (aWordProcessor.AreLeftOvers) { if (aWordProcessor.LeftOvers.Start > aParagraphRange.End) { continue; // skip to the next paragraph } nCharIndex = aWordProcessor.LeftOvers.StartIndex; aWordProcessor.LeftOvers = null; // turn off "left overs" } WordRange aThisParagraph = new WordRange(aParagraphRange); if (!ProcessRangeAsWords(aWordProcessor, aThisParagraph, nCharIndex)) { return(false); } } return(true); }
public bool ProcessWholeParagraphs(OfficeDocumentProcessor aWordProcessor, Word.Paragraphs aParagraphs) { foreach (Word.Paragraph aParagraph in aParagraphs) { // get the Range object for this paragraph Word.Range aParagraphRange = aParagraph.Range; // if we're picking up where we left off and we're not there yet... int nCharIndex = 0; if (aWordProcessor.AreLeftOvers) { if (aWordProcessor.LeftOvers.Start > aParagraphRange.End) { continue; // skip to the next paragraph } nCharIndex = aWordProcessor.LeftOvers.StartIndex; aWordProcessor.LeftOvers = null; // turn off "left overs" } WordRange aRunRange = new WordRange(aParagraphRange); int nEndIndex = --aRunRange.EndIndex; int nLastIndex = nCharIndex; // exclude the paragraph end and process it as a whole unit (we may have to do this multiple times bool bStop = false; while (!bStop && (nCharIndex < nEndIndex)) { aRunRange.StartIndex = nCharIndex; if (aRunRange.EndIndex != nEndIndex) { aRunRange.EndIndex = nEndIndex; } nLastIndex = nCharIndex; System.Diagnostics.Trace.WriteLine(String.Format("Start: {0}, End: {1}, text: {2}, length: {3}", aRunRange.Start, aRunRange.End, aRunRange.Text, aRunRange.Text.Length)); if (!aWordProcessor.Process(aRunRange, ref nCharIndex)) { aWordProcessor.LeftOvers = aRunRange; return(false); } if (nLastIndex == nCharIndex) { break; } } } return(true); }
public bool ProcessParagraphsIsoFormat(OfficeDocumentProcessor aWordProcessor, Word.Paragraphs aParagraphs) { foreach (Word.Paragraph aParagraph in aParagraphs) { // get the Range object for this paragraph Word.Range aParagraphRange = aParagraph.Range; // if we're picking up where we left off and we're not there yet... int nCharIndex = 0; if (aWordProcessor.AreLeftOvers) { if (aWordProcessor.LeftOvers.Start > aParagraphRange.End) { continue; // skip to the next paragraph } nCharIndex = aWordProcessor.LeftOvers.StartIndex; aWordProcessor.LeftOvers = null; // turn off "left overs" } WordRange aRunRange = new WordRange(aParagraphRange); int nStartIndex = aRunRange.StartIndex; System.Diagnostics.Debug.Assert(nStartIndex == 0); // if we have mixed character formatting, then use a binary search algorithm to find // the maximum run. if (MixedCharacterFormatting(aRunRange)) { int nWidth = aRunRange.EndIndex / 2; do { aRunRange.EndIndex++; } while (!MixedCharacterFormatting(aRunRange)); aRunRange.EndIndex--; // back up one } else { // the whole paragraph seems to be iso formatted, so exclude the paragraph end and // process it as a whole unit aRunRange.EndIndex--; if (!aWordProcessor.Process(aRunRange, ref nCharIndex)) { aWordProcessor.LeftOvers = aRunRange; return(false); } } } return(true); }
public override bool ProcessWordByWord(OfficeDocumentProcessor aWordProcessor) { // if multiple paragraphs... int nCharIndex = 0; WordParagraphs aParagraphRanges = new WordParagraphs(Document.Application.Selection); foreach (Word.Range aRange in aParagraphRanges) { WordRange aThisParagraph = new WordRange(aRange); if (!ProcessRangeAsWords(aWordProcessor, aThisParagraph, nCharIndex)) { return(false); } } return(true); }
public void ReplaceText(WordRange aRunRange, string strNewText) { try { aRunRange.Text = strNewText; } catch (Exception ex) { if (ex.Message == "The range cannot be deleted.") { aRunRange.End++; string strText = aRunRange.Text; if (strText[strText.Length - 1] == '\n') { throw new ApplicationException(String.Format("The paragraphs in this document end with both a carriage return (i.e. \\r) and a line feed character (i.e. \\n).{0}You need to change your 'Find what' string to include both (i.e. '\r\n'), or the found text can't be replaced.", Environment.NewLine)); } } } }
public WordRange(WordRange basedOnRange) { m_theRangeBasedOn = basedOnRange.m_theRangeBasedOn.Duplicate; // for some unknown reason, the Range sometimes doesn't operate the way one would expect // an array to work. Sometimes (I can't tell why) the first "character" (which is always // available from Text[0]), comes from a Start value of 0 and sometimes from 1. // In the latter case, I think something hidden must be at 0 and the first "character" // is really coming from offset 1. // Anyway, the good news is that I think I can detect this case as follows: // The offset to add to the Start and End properties is one less than the End property // value of the first "Character". For the weird case, the Start is 0 and the End 2, but // the length and Text is only 1!? // But only if this isn't a zero-length range if (m_theRangeBasedOn.Start != m_theRangeBasedOn.End) { Word.Range aRange = m_theRangeBasedOn.Characters.First; m_nOffset = aRange.End - aRange.Text.Length; } }
public void ProcessParagraphsFindAndReplace(FindWordProcessor aWordProcessor, Word.Range theRangeToSearch, BackgroundWorker worker) { // get Range object we can manipulate for searching (i.e. don't muck with the original WordRange aRunRange = GetStartingRange(theRangeToSearch); if (aRunRange == null) { return; } // how many paragraphs ahead we have to look for a match int nEndRangeToSearch = -1; // might change from loop to loop if we do replacements (so update inside loop) object nOffsetParagraph = aWordProcessor.NumOfParagraphsToSearch - 1; do { SearchAreaStart = aRunRange.Start; // this updates the progress bar worker.ReportProgress(SearchAreaStart); // if the search string contains multiple '\r's, then we have to look ahead that many paragraphs int SearchAreaEnd; nEndRangeToSearch = theRangeToSearch.End; // might change from loop to loop if we do replacements if (0 < (int)nOffsetParagraph) { Word.Paragraph aEndParagraph = m_paraSearch.Next(ref nOffsetParagraph); if (aEndParagraph == null) { return; // means we can't possibly find it now } SearchAreaEnd = Math.Min(aEndParagraph.Range.End, nEndRangeToSearch); aRunRange.End = SearchAreaEnd; } else { int nEndOfRun = aRunRange.End; if (nEndOfRun > nEndRangeToSearch) { aRunRange.End = SearchAreaEnd = nEndRangeToSearch; } else { SearchAreaEnd = nEndOfRun; } } // loop until the end of the paragraph and process it as a whole unit (we may have to do this multiple times) bool bGoToNext = true; while (SearchAreaStart < SearchAreaEnd) { // keep track of the last index so we can tell whether anything was found or not FindWordProcessor.FindResult res = aWordProcessor.FindReplaceCompare(aRunRange, ref SearchAreaStart, ref FoundAreaLength); // if we found it and need to stop (or if the user had cancelled the search)... bGoToNext = true; // assume we will if ((res == FindWordProcessor.FindResult.eFindFound) || worker.CancellationPending) { return; } // otherwise, it might have been a replacement and we have to update the end of the paragraph // value and the end of the range and do it again (e.g. find the next occurrence to replace) else if (res == FindWordProcessor.FindResult.eReplaceFound) { // we might have actually removed the paragraph mark, in which case the next paragraph // is now part of the current paragraph. Unfortunately, the aParagraphRange isn't // updated. // if the search string contained '\r's, then we have to look ahead all over again if (aWordProcessor.IsAnyCRs) { bGoToNext = false; break; } else { Word.Range aParagraphRange = m_paraSearch.Range; SearchAreaEnd = aParagraphRange.End; aRunRange.End = SearchAreaEnd; aRunRange.Start = SearchAreaStart; } } // otherwise, it means we didn't find it here and we're done with this paragraph else { System.Diagnostics.Debug.Assert(!aWordProcessor.IsFound); break; } } // advance to the next paragraph if (bGoToNext) { m_paraSearch = m_paraSearch.Next(ref offset1); } if (m_paraSearch != null) { aRunRange = new WordRange(m_paraSearch.Range); // in the case we replace some CRs, we re-start where we left off (after the replaced text) if (!bGoToNext) { aRunRange.Start = SearchAreaStart; } } } while ((m_paraSearch != null) && (aRunRange.Start < nEndRangeToSearch)); }
// determine if this section of text (some portion of one or more paragraphs) has the find what text in it public FindResult FindReplaceCompare(WordRange aRunRange, ref int SearchAreaStart, ref int FoundAreaLength) { FindResult res = FindResult.eNothingFound; string strInput = aRunRange.Text; if (String.IsNullOrEmpty(strInput) || (m_aECRegex == null)) { return(res); } // otherwise 'convert' it to see if the 'Find what' string is in it string strOutput = m_aECRegex.Convert(strInput); // if the input string is different from the output string, then the FindWhat string was found. if (strInput != strOutput) { #if !DefineToNotUseSplitAndConvert // The way the convert works is that it will replace each instance of the input string that matches // the FindWhat syntax (i.e. there may be more than one replacement we have to deal with). // here's the problem: if there was more than one replacment, then I really can't tell what portion // of the FindWhat text each replacement goes with. Consider the string "ababbbbbabb". If the 'Find // what' string is "ab+", then it breaks up to: "ab", "abbbbb", and "abb", but if these three are // right in a row and the replacement is "", then the resulting strOutput will be <delim><delim>... // repeated 3 times. So there's no way to tell which portion of the input text corresponds to which // portion of the output text. My original stab at it just treated them evenly and divide by the // number of consecutive replacements... this works if the 'FindWhat' is something like "ab". But all // bets are off if the user uses the "+" (eat-em-up) expression code. // I think the only solution is to always only deal with a single replacement... So... if we have more // than one match in a particular output (which we can tell by the Length of astrSegments), do a binary // search until we have only one. string[] astrSegments = GetFirstReplacementSplitArray(ref strInput, ref strOutput); // get the index to the first character of the replacement (which is the same as the first character // of the 'Find what' string as well). int nIndex = astrSegments[0].Length; // remember this so we pick up here later SearchAreaStart += nIndex; if (nIndex > 0) { aRunRange.Start = SearchAreaStart; } // the replacement string is easy. It's just whatever's in the one'th element of the Split array. string strReplacementString = astrSegments[1]; // There might be some stuff between the end of the replacement and the end of the input string. string strStuffFollowingMatch = astrSegments[2]; // may be null // get the index to the end of the 'Find what' string int nEndOfFindWhatSelection; if (String.IsNullOrEmpty(strStuffFollowingMatch)) { nEndOfFindWhatSelection = strInput.Length; } else { nEndOfFindWhatSelection = strInput.Length - strStuffFollowingMatch.Length; } #else // this could probably be done more elegantly with Split rather than what I do in the #else case string[] astrSegments = strOutput.Split(m_achDelimiter); // must be odd (before, ||: replace, following :||), where any or all can be null int nTotalSegments = astrSegments.Length; System.Diagnostics.Debug.Assert((nTotalSegments % 2) == 1); // get the index to the first character of the replacement (which is the same as the first character // of the 'Find what' as well). int nIndex = astrSegments[0].Length; // remember this so we pick up here later SearchAreaStart += nIndex; if (nIndex > 0) { aRunRange.Start = SearchAreaStart; } // the replacement string is easy. It's just whatever's in the 1st element of the Split array. // but we have to figure out what the 'Find what' text is so that we can select it (so we can replace // it). This is not so easy, because it could be anything and not just a string of text like in a normal // find. string strReplacementString = astrSegments[1]; // Between the end of the first replacement and the beginning of the next (if multiple replacments) // is a string which should match something in the original, which we can search for string strStuffFollowingMatch = astrSegments[2]; // may be null int nEndOfFindWhatSelection; if (String.IsNullOrEmpty(strStuffFollowingMatch)) { // If the 'Find what' is repeated twice in a row, then the stuff in-between the two instances of // replacement text will be null. // Detect this by looking at the length of the even number string array elements (2, 4, etc), // which are the segments following the replacements. This tells us what we have to divide by // to get the proportion for only one find. int nNumReplacmentsInARow = 1; int nNextReplacementIndex = 2; nTotalSegments--; while ((nNextReplacementIndex < nTotalSegments) && String.IsNullOrEmpty(astrSegments[nNextReplacementIndex])) { nNumReplacmentsInARow++; nNextReplacementIndex += 2; } if (nNextReplacementIndex < astrSegments.Length) { strStuffFollowingMatch = astrSegments[nNextReplacementIndex]; } int numerator; if (String.IsNullOrEmpty(strStuffFollowingMatch)) { numerator = strInput.Length; } else { numerator = strInput.IndexOf(strStuffFollowingMatch, nIndex + 1); } nEndOfFindWhatSelection = ((numerator - nIndex) / nNumReplacmentsInARow) + nIndex; } else { nEndOfFindWhatSelection = strInput.IndexOf(strStuffFollowingMatch, nIndex + 1); } /* * int nIndex = strOutput.IndexOf(m_achDelimiter[0]); * System.Diagnostics.Debug.Assert(nIndex != -1); * SearchAreaStart += nIndex; * if (nIndex > 0) * aRunRange.Start = SearchAreaStart; * * int nEndOfReplacement = strOutput.IndexOf(m_achDelimiter[0], nIndex + 1); * System.Diagnostics.Debug.Assert(nEndOfReplacement != -1); * * // the replacement string is what's between these two * string strReplacementString = strOutput.Substring(nIndex + 1, (nEndOfReplacement - nIndex - 1)); * * // now the complicated part. Between the end of the first replacement and the next * // one is a string which should match something in the original. But if the replacement * // were null, then it could be the very next character... * // This also handles the situation where there may be several "found whats" * int nNumReplacmentsInARow = 1; * int nNextIndex = nEndOfReplacement + 1; * while ((nNextIndex < strOutput.Length) && (strOutput[nNextIndex] == m_achDelimiter[0])) * { * nNumReplacmentsInARow++; * nEndOfReplacement = strOutput.IndexOf(m_achDelimiter[0], nNextIndex + 1); * nNextIndex = nEndOfReplacement + 1; * } * * if (nNextIndex < strOutput.Length) * { * nEndOfReplacement = strOutput.IndexOf(m_achDelimiter[0], nNextIndex + 1); * if (nEndOfReplacement == -1) * nEndOfReplacement = strOutput.Length; * } * else if (nNextIndex == strOutput.Length) * nNextIndex--; * * string strStuffFollowingMatch = strOutput.Substring(nNextIndex, nEndOfReplacement - nNextIndex); * * nEndOfFindWhatSelection = ((strInput.Length - nIndex) / nNumReplacmentsInARow) + nIndex; * if (!String.IsNullOrEmpty(strStuffFollowingMatch)) * { * nEndOfFindWhatSelection = strInput.IndexOf(strStuffFollowingMatch, nIndex + 1) / nNumReplacmentsInARow; * } */ #endif // !UseSplitToFindReplacements FoundAreaLength = nEndOfFindWhatSelection - nIndex; aRunRange.End = SearchAreaStart + FoundAreaLength; // if we're doing ReplaceAll or Replace when the FindWhat string has been found... System.Diagnostics.Debug.Assert(FormButton != FormButtons.Cancel); // means it wasn't initialized if (!DontReplaceOnNextFind && ((ReplaceAll) || ((FormButton == FormButtons.ReplaceOnce) && (nIndex == 0)))) { if (FormButton == FormButtons.ReplaceOnce) { DontReplaceOnNextFind = true; // so we do a virtual find next after this } // this means replace the word in situ, with what was converted ReplaceText(aRunRange, strReplacementString); NumOfReplacements++; // start the next search after this replaced value string strReplacedText = aRunRange.Text; // this may not be exactly the same as strReplace (e.g. after replacing final '\r') if (!String.IsNullOrEmpty(strReplacedText)) { FoundAreaLength = strReplacedText.Length; SearchAreaStart += FoundAreaLength; } res = FindResult.eReplaceFound; } else { // select just the search string and return as if cancelled so the outer loop can prompt for it. IsFound = true; // select the FindWhat text found aRunRange.Select(); res = FindResult.eFindFound; } } else if (FormButton == FormButtons.ReplaceOnce) { // otherwise, if the user clicked ReplaceOnce and we didn't find it right away, // then change it to function like Find instead. DontReplaceOnNextFind = true; // so we do a virtual find next after this } return(res); }