/// ------------------------------------------------------------------------------------ /// <summary> /// Given a Scripture section, this method returns the index of the paragraph containing /// the requested verse and the position of the character immediately following the /// verse number in that paragraph. If an exact match isn't found, the closest /// approximate place is found. /// </summary> /// <param name="section">The section whose paragraphs will be searched</param> /// <param name="targetRef">The reference being sought</param> /// <param name="iPara">The index of the paragraph where the verse was found</param> /// <param name="ichPosition">The index of the character immediately following the /// verse number in that paragraph</param> /// ------------------------------------------------------------------------------------ protected void FindVerseNumber(IScrSection section, ScrReference targetRef, out int iPara, out int ichPosition) { iPara = 0; ichPosition = 0; bool fChapterFound = (BCVRef.GetChapterFromBcv(section.VerseRefMin) == targetRef.Chapter); foreach (IStTxtPara para in section.ContentOA.ParagraphsOS) { if (para.Contents.Text == null) { continue; } TsRunInfo tsi; ITsTextProps ttpRun; int ich = 0; while (ich < para.Contents.Length) { // Get props of current run. ttpRun = para.Contents.FetchRunInfoAt(ich, out tsi); // See if it is our verse number style. if (fChapterFound) { if (ttpRun.Style() == ScrStyleNames.VerseNumber) { // The whole run is the verse number. Extract it. string sVerseNum = para.Contents.Text.Substring(tsi.ichMin, tsi.ichLim - tsi.ichMin); int startVerse, endVerse; ScrReference.VerseToInt(sVerseNum, out startVerse, out endVerse); if ((targetRef.Verse >= startVerse && targetRef.Verse <= endVerse) || targetRef.Verse < startVerse) { ichPosition = tsi.ichLim; return; } } } // See if it is our chapter number style. else if (ttpRun.Style() == ScrStyleNames.ChapterNumber) { try { // Assume the whole run is the chapter number. Extract it. string sChapterNum = para.Contents.Text.Substring(tsi.ichMin, tsi.ichLim - tsi.ichMin); fChapterFound = (ScrReference.ChapterToInt(sChapterNum) == targetRef.Chapter); } catch (ArgumentException) { // ignore runs with invalid Chapter numbers } } ich = tsi.ichLim; } iPara++; } iPara = 0; // Couldn't find it. }
/// ------------------------------------------------------------------------------------ /// <summary> /// Updates the end of verse to include any adjacent, duplicate verse numbers. /// For any adjacent and duplicate verses in the paragraph, we want to include them in /// the text of the same ScrVerse.(TE-7137) /// </summary> /// <returns>ich for the end of the ScrVerse updated (perhaps) to include the verse /// number and text of duplicate and adjacent verses</returns> /// ------------------------------------------------------------------------------------ private int UpdateEndOfVerse() { if (m_inChapterNum) { return(m_ich); } bool fFoundChapterNumber; int iRun = FindNextVerseRun(m_ich, out fFoundChapterNumber); // If we found another chapter or verse number run... if (iRun > 0) { if (fFoundChapterNumber) { m_ich = m_tssParaContents.get_LimOfRun(iRun - 1); // update the end of the verse return(m_ich); } // Scan through the remainder of the paragraph (or until a verse number run // is not found, in which case, the iRun will be set to -1) for (; iRun > 0 && iRun < m_tssParaContents.RunCount;) { int verseStart, verseEnd; ScrReference.VerseToInt(m_tssParaContents.get_RunText(iRun), out verseStart, out verseEnd); // If the starting reference of the next verse number run is the same as the // current starting reference... if (verseStart == m_startRef.Verse) { // we have a duplicate verse and we need to find either the next verse number // run or the end of the paragraph to get the remainder of the ScrVerse. iRun = FindNextVerseRun(m_tssParaContents.get_LimOfRun(iRun), out fFoundChapterNumber); m_ich = iRun < 0 ? m_tssParaContents.Text.Length : m_tssParaContents.get_LimOfRun(iRun - 1); // update the end of the verse } else { m_ich = m_tssParaContents.get_MinOfRun(iRun); break; // no more duplicate, adjacent verses. We're finished scanning the para. } } } // No more verse numbers found. Set end of ScrVerse if we are not in a chapter number run. else { m_ich = m_tssParaContents.Text != null ? m_tssParaContents.Text.Length : 0; } return(m_ich); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Given a Scripture section, this method returns the index of the paragraph containing /// the requested verse and the position of the character immediately following the /// verse number in that paragraph. If an exact match isn't found, the closest /// approximate place is found. /// </summary> /// <param name="section">The section whose paragraphs will be searched</param> /// <param name="targetRef">The reference being sought</param> /// <param name="ihvoPara"></param> /// <param name="ichPosition"></param> /// <remarks>Currently, this does NOT attempt to find a close match, but some day it /// should</remarks> /// ------------------------------------------------------------------------------------ protected void FindVerseNumber(ScrSection section, ScrReference targetRef, out int ihvoPara, out int ichPosition) { ihvoPara = 0; ichPosition = 0; bool fChapterFound = ((ScrReference)section.VerseRefStart).Chapter == targetRef.Chapter; foreach (StTxtPara para in section.ContentOA.ParagraphsOS) { TsStringAccessor contents = para.Contents; TsRunInfo tsi; ITsTextProps ttpRun; int ich = 0; while (ich < contents.Text.Length) { // Get props of current run. ttpRun = contents.UnderlyingTsString.FetchRunInfoAt(ich, out tsi); // See if it is our verse number style. if (fChapterFound) { if (StStyle.IsStyle(ttpRun, ScrStyleNames.VerseNumber)) { // The whole run is the verse number. Extract it. string sVerseNum = contents.Text.Substring(tsi.ichMin, tsi.ichLim - tsi.ichMin); int startVerse, endVerse; ScrReference.VerseToInt(sVerseNum, out startVerse, out endVerse); if (targetRef.Verse >= startVerse && targetRef.Verse <= endVerse) { ihvoPara = para.OwnOrd - 1; ichPosition = tsi.ichLim; return; } // TODO: Currently, this does NOT attempt to detect when we have // a close match } } // See if it is our chapter number style. else if (StStyle.IsStyle(ttpRun, ScrStyleNames.ChapterNumber)) { // Assume the whole run is the chapter number. Extract it. string sChapterNum = contents.Text.Substring(tsi.ichMin, tsi.ichLim - tsi.ichMin); int nChapter = ScrReference.ChapterToInt(sChapterNum); fChapterFound = (nChapter == targetRef.Chapter); } ich = tsi.ichLim; } } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Advances the enumerator to the next verse in the paragraph. /// </summary> /// <returns>True if we successfully moved to the next ScrVerse; False if we reached /// the end of the paragraph.</returns> /// ------------------------------------------------------------------------------------ public bool MoveNext() { InitializeParaContents(); if (m_ich > m_paraLength) { return(false); } m_ichVerseStart = m_ichTextStart = m_ich; TsRunInfo tsi; ITsTextProps ttpRun; string sPara = m_tssParaContents.Text; int nChapter = -1; // This is used to see if we found a chapter later. m_inVerseNum = false; m_inChapterNum = false; while (m_ich < m_paraLength) { ttpRun = m_tssParaContents.FetchRunInfoAt(m_ich, out tsi); // If this run is our verse number style if (ttpRun.Style() == ScrStyleNames.VerseNumber) { // If there is already a verse in process, a new verse number run will terminate it. if (m_ichVerseStart != m_ich) { break; } // Assume the whole run is the verse number string sVerseNum = sPara.Substring(m_ich, tsi.ichLim - tsi.ichMin); int nVerseStart, nVerseEnd; ScrReference.VerseToInt(sVerseNum, out nVerseStart, out nVerseEnd); m_startRef.Verse = nVerseStart; m_endRef.Verse = nVerseEnd; m_ichVerseStart = m_ich; //set VerseStart at beg of verse number m_ich += sVerseNum.Length; m_ichTextStart = m_ich; m_inVerseNum = true; } // If this run is our chapter number style else if (ttpRun.Style() == ScrStyleNames.ChapterNumber) { // If there is already a verse being processed, then the chapter number // run will end it if (m_ichVerseStart != m_ich) { break; } try { // Assume the whole run is the chapter number string sChapterNum = sPara.Substring(m_ich, tsi.ichLim - tsi.ichMin); nChapter = ScrReference.ChapterToInt(sChapterNum); m_startRef.Chapter = m_endRef.Chapter = nChapter; // Set the verse number to 1, since the first verse number after a // chapter is optional. If we happen to get a verse number in the // next run, this '1' will be overridden (though it will probably // still be a 1). m_startRef.Verse = m_endRef.Verse = 1; m_ichVerseStart = m_ich; //set VerseStart at beg of chapter number m_ich += sChapterNum.Length; m_ichTextStart = m_ich; m_inChapterNum = true; } catch (ArgumentException) { // ignore runs with invalid Chapter numbers m_ich += tsi.ichLim - tsi.ichMin; } } else // Process a text run. { // If it comes after a chapter number, then just return the // chapter number without adding the text. if (nChapter > 0) { break; } // skip to the next run m_ich += tsi.ichLim - tsi.ichMin; } } // determine if this verse is a complete paragraph, an empty para and/or a stanza break. m_isCompletePara = (m_ichVerseStart == 0 && m_ich == m_paraLength); if (string.IsNullOrEmpty(sPara)) { //m_isEmptyPara = true; m_isStanzaBreak = string.Equals(ScrStyleNames.StanzaBreak, m_para.StyleName); } try { return((m_ich > m_ichVerseStart) || FirstTimeAtStanzaBreak); } finally { // Update the previous paragraph for the next time (but we do the update // in a 'finally' so that we can compare the current to the previous for // the return value). m_prevPara = m_para; } }
/// ------------------------------------------------------------------------------------ /// <summary> /// Updates the end of verse to include any adjacent, duplicate verse numbers. /// For any adjacent and duplicate verses in the paragraph, we want to include them in /// the text of the same ScrVerse.(TE-7137) /// </summary> /// <returns>ich for the end of the ScrVerse updated (perhaps) to include the verse /// number and text of duplicate and adjacent verses</returns> /// ------------------------------------------------------------------------------------ private int UpdateEndOfVerse() { if (m_fTreatChapterNumberAsSeparateScrVerse && m_inChapterNum) { return(m_ich); } bool fFoundChapterNumber; int iRun = FindNextVerseRun(m_ich, out fFoundChapterNumber); // If we found another chapter or verse number run... if (iRun > 0) { if (fFoundChapterNumber) { m_ich = m_tssParaContents.get_LimOfRun(iRun - 1); // update the end of the verse return(m_ich); } // Scan through the remainder of the paragraph (or until a verse number run // is not found, in which case, the iRun will be set to -1) for (; iRun > 0 && iRun < m_tssParaContents.RunCount;) { int verseStart, verseEnd; ScrReference.VerseToInt(m_tssParaContents.get_RunText(iRun), out verseStart, out verseEnd); // The following sort of seems like it might be a better condition for handling // the special case of a chapter number followed by a verse number other than // 1, but it causes several tests to fail in the BookMergerTests. Maybe a // better solution is to not combine chapter numbers with following verse numbers // at all and instead detect and handle this as a special case in the // DetectDifferences code. //if ((verseStart == m_startRef.Verse && verseEnd == m_endRef.Verse) || // (m_inChapterNum && iRun == m_tssParaContents.get_RunAt(m_ichVerseStart) + 1)) // If the starting reference of the next verse number run is the same as the // current starting reference... if (verseStart == m_startRef.Verse) { // We have either a duplicate verse or a verse immediately following a // chapter number and we need to find either the next verse number iRun = FindNextVerseRun(m_tssParaContents.get_LimOfRun(iRun), out fFoundChapterNumber); m_ich = iRun < 0 ? m_tssParaContents.Text.Length : m_tssParaContents.get_LimOfRun(iRun - 1); // update the end of the verse m_inVerseNum = true; if (m_inChapterNum) { m_startRef.Verse = verseStart; m_endRef.Verse = verseEnd; } } else { m_ich = m_tssParaContents.get_MinOfRun(iRun); break; // no more duplicate, adjacent verses. We're finished scanning the para. } } } else { // No more verse numbers found. Set end of ScrVerse if we are not // in a chapter number run. m_ich = m_tssParaContents.Text != null ? m_tssParaContents.Text.Length : 0; } return(m_ich); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Find the start and end reference, by searching backwards in the given ITsString /// from the given position. /// </summary> /// <param name="tss">the given ITsString</param> /// <param name="ichPos">Index of character in paragraph whose reference we want</param> /// <param name="fAssocPrev">Consider this position to be associated with any preceding /// text in the paragraph (in the case where ichPos is at a chapter boundary).</param> /// <param name="refStart">[out] Start reference for the paragraph.</param> /// <param name="refEnd">[out] End reference for the paragraph.</param> /// <returns>A value of <see cref="ChapterVerseFound"/> that tells if a chapter and/or /// verse number was found in this paragraph.</returns> /// <remarks>If ichPos LT zero, we will not search this para, and simply return. /// Be careful not to use this method to search the contents of paragraph using /// character offsets from the BT! /// </remarks> /// ------------------------------------------------------------------------------------ static public ChapterVerseFound GetBCVRefAtPosWithinTss(ITsString tss, int ichPos, bool fAssocPrev, out BCVRef refStart, out BCVRef refEnd) { refStart = new BCVRef(); refEnd = new BCVRef(); ChapterVerseFound retVal = ChapterVerseFound.None; if (tss.Length <= 0) { return(retVal); } TsRunInfo tsi; ITsTextProps ttpRun; bool fGotVerse = false; int ich = ichPos; if (ich > tss.Length) { ich = tss.Length; } while (ich >= 0) { // Get props of current run. ttpRun = tss.FetchRunInfoAt(ich, out tsi); // If we're at (the front edge of) a C/V number boundary and the // caller said to associate the position with the previous material, then // ignore this run unless we're at the beginning of the para. // The run is actually the *following* run, which we don't care about. if (!fAssocPrev || ichPos <= 0 || ichPos != tsi.ichMin) { // See if it is our verse number style. if (!fGotVerse && StStyle.IsStyle(ttpRun, ScrStyleNames.VerseNumber)) { // The whole run is the verse number. Extract it. string sVerseNum = tss.get_RunText(tsi.irun); // string sVerseNum = tss.Text.Substring(tsi.ichMin, // tsi.ichLim - tsi.ichMin); int startVerse, endVerse; ScrReference.VerseToInt(sVerseNum, out startVerse, out endVerse); refStart.Verse = startVerse; refEnd.Verse = endVerse; fGotVerse = true; retVal = ChapterVerseFound.Verse; } // See if it is our chapter number style. else if (StStyle.IsStyle(ttpRun, ScrStyleNames.ChapterNumber)) { try { // Assume the whole run is the chapter number. Extract it. string sChapterNum = tss.get_RunText(tsi.irun); int nChapter = ScrReference.ChapterToInt(sChapterNum); refStart.Chapter = refEnd.Chapter = nChapter; if (fGotVerse) { // Found a chapter number to go with the verse number we // already found, so build the full reference using this // chapter with the previously found verse (already set). retVal |= ChapterVerseFound.Chapter; } else { // Found a chapter number but no verse number, so assume the // edited text is in verse 1 of the chapter. refStart.Verse = refEnd.Verse = 1; fGotVerse = true; retVal = ChapterVerseFound.Chapter | ChapterVerseFound.Verse; } break; } catch (ArgumentException) { // ignore runs with invalid Chapter numbers } } } // move index (going backwards) to the character just before the Min of the run // we just looked at ich = tsi.ichMin - 1; } return(retVal); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Finds all ORCs in the given text and notes any orphaned footnotes or pictures. /// </summary> /// <param name="text">The text.</param> /// <param name="textLocationInfo">Additional information about the location of the /// text that can help the user find it.</param> /// <param name="startRef">Caller should pass in the initial reference to use as the /// basis for any references found in the course of parsing the text. Returned value /// will be the final reference found, which can be used as the basis for the subsequent /// text</param> /// <param name="endRef">Same as startRef, except in the case of verse bridges or /// section headings</param> /// <param name="footnotes">List of footnotes owned by the book that owns the /// given text. As footnotes are found, their locations will be set.</param> /// <param name="parasWithOrcs">List of paragraphs and ORC positions (excluding known /// picture ORCs).</param> /// <returns></returns> /// ------------------------------------------------------------------------------------ private static bool FindOrcsWithoutPropsInText(IStText text, string textLocationInfo, ref BCVRef startRef, ref BCVRef endRef, List <FootnoteOrcLocation> footnotes, List <OrcLocation> parasWithOrcs) { bool foundOrphan = false; foreach (IStTxtPara para in text.ParagraphsOS) { ITsString tssContents = para.Contents; string sContents = tssContents.Text; if (sContents == null) { continue; } int nRun = tssContents.RunCount; for (int i = 0; i < nRun; i++) { TsRunInfo runInfo; ITsTextProps tprops = tssContents.FetchRunInfo(i, out runInfo); string styleName = tprops.GetStrPropValue( (int)FwTextPropType.ktptNamedStyle); // When a verse number is encountered, save the number into // the reference. if (styleName == ScrStyleNames.VerseNumber) { string sVerseNum = sContents.Substring(runInfo.ichMin, runInfo.ichLim - runInfo.ichMin); int nVerseStart, nVerseEnd; ScrReference.VerseToInt(sVerseNum, out nVerseStart, out nVerseEnd); startRef.Verse = nVerseStart; endRef.Verse = nVerseEnd; } // If a chapter number is encountered then save the number into // the reference and start the verse number back at 1. else if (styleName == ScrStyleNames.ChapterNumber) { try { string sChapterNum = sContents.Substring(runInfo.ichMin, runInfo.ichLim - runInfo.ichMin); startRef.Chapter = endRef.Chapter = ScrReference.ChapterToInt(sChapterNum); startRef.Verse = endRef.Verse = 1; } catch (ArgumentException) { // ignore runs with invalid Chapter numbers } } else { // search contents for ORCs for (int ich = runInfo.ichMin; ich < runInfo.ichLim; ich++) { if (sContents[ich] != StringUtils.kChObject) { continue; } OrcLocation orcLocation = new OrcLocation(para, ich, startRef, endRef, textLocationInfo); ITsTextProps props = tssContents.get_PropertiesAt(ich); string objData = props.GetStrPropValue((int)FwTextPropType.ktptObjData); if (objData == null) { foundOrphan = true; } else { // first char. of strData is type code - GUID will follow it. Guid objGuid = MiscUtils.GetGuidFromObjData(objData.Substring(1)); ICmObject obj; if (!text.Cache.ServiceLocator.ObjectRepository.TryGetObject(objGuid, out obj)) { foundOrphan = true; } else if (obj.ClassID == ScrFootnoteTags.kClassId) { foreach (FootnoteOrcLocation footnote in footnotes) { if (footnote.footnote.Guid == objGuid) { orcLocation.Footnote = footnote.footnote; footnote.location = orcLocation; break; } } } else { Debug.Assert(obj.ClassID == CmPictureTags.kClassId, "Unknown class id in embedded object: " + obj.ClassID); continue; } } parasWithOrcs.Add(orcLocation); } } } } return(foundOrphan); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Gets the reference for this footnote. /// </summary> /// <param name="owningBook">The owning book.</param> /// <param name="para">The para to search for a Scripture reference (verse or chapter). /// </param> /// <param name="startRef">The starting reference for this footnote (updated in this /// method).</param> /// <param name="endRef">The ending reference for this footnote (updated in this /// method).</param> /// <returns></returns> /// ------------------------------------------------------------------------------------ private RefResult GetReference(IScrBook owningBook, IScrTxtPara para, BCVRef startRef, BCVRef endRef) { bool foundSelf = (para != ParaContainingOrcRA); IStFootnoteRepository footnoteRepo = Cache.ServiceLocator.GetInstance <IStFootnoteRepository>(); ITsString tssContents = para.Contents; for (int i = tssContents.RunCount - 1; i >= 0; i--) { string styleName = tssContents.get_StringProperty(i, (int)FwTextPropType.ktptNamedStyle); if (foundSelf && styleName == ScrStyleNames.VerseNumber && startRef.Verse == 0) { int nVerseStart, nVerseEnd; ScrReference.VerseToInt(tssContents.get_RunText(i), out nVerseStart, out nVerseEnd); startRef.Verse = nVerseStart; endRef.Verse = nVerseEnd; } else if (foundSelf && styleName == ScrStyleNames.ChapterNumber && startRef.Chapter == 0) { try { startRef.Chapter = endRef.Chapter = ScrReference.ChapterToInt(tssContents.get_RunText(i)); } catch (ArgumentException) { // ignore runs with invalid Chapter numbers } if (startRef.Verse == 0) { startRef.Verse = endRef.Verse = 1; } } else if (styleName == null) { IScrFootnote footnote = (IScrFootnote)footnoteRepo.GetFootnoteFromObjData(tssContents.get_StringProperty(i, (int)FwTextPropType.ktptObjData)); if (footnote != null) { if (footnote == this) { foundSelf = true; continue; } RefRange otherFootnoteLocation = ((ScrFootnote)footnote).FootnoteRefInfo_Internal; if (foundSelf && otherFootnoteLocation != RefRange.EMPTY) { // Found another footnote with a reference we can use if (startRef.Verse == 0) { startRef.Verse = otherFootnoteLocation.StartRef.Verse; endRef.Verse = otherFootnoteLocation.EndRef.Verse; } if (startRef.Chapter == 0) { startRef.Chapter = otherFootnoteLocation.StartRef.Chapter; endRef.Chapter = otherFootnoteLocation.EndRef.Chapter; } } else if (foundSelf) { // Previous footnote does not have a reference yet. We presume, for performance // reasons, that none of the previous footnotes have valid references yet, so // we set all the footnotes for the book. ((ScrBook)owningBook).RefreshFootnoteRefs(); return(RefResult.ScannedAllFootnotes); } } } if (startRef.Verse != 0 && endRef.Verse != 0 && startRef.Chapter != 0 && endRef.Chapter != 0) { return(RefResult.Found); } } return(RefResult.NotFound); }