/// <summary> /// 計算折行之後的縮排格數。 /// </summary> /// <param name="brLine"></param> /// <returns>縮排格數。</returns> private static int CalcNewLineIndents(BrailleLine brLine) { if (AppGlobals.Config.Braille.AutoIndentNumberedLine) { int count = 0; bool foundOrderedItem = false; // 如果是以數字編號開頭(空白略過),自動計算折行的列要縮排幾格。 foreach (BrailleWord brWord in brLine.Words) { if (BrailleWord.IsBlank(brWord)) { count++; continue; } if (BrailleWord.IsOrderedListItem(brWord)) { count++; foundOrderedItem = true; break; } } if (foundOrderedItem) { return(count); } } return(0); }
private void Indent(BrailleLine brLine, int indents) { for (int i = 0; i < indents; i++) { brLine.Insert(0, BrailleWord.NewBlank()); } }
/// <summary> /// 移除代表數字的點位。 /// </summary> /// <param name="brLine"></param> /// <param name="beginIdx"></param> /// <param name="endIdx"></param> public static void RemoveDigitCell(BrailleLine brLine, int beginIdx, int endIdx) { if (beginIdx < 0 || endIdx < 0 || beginIdx > endIdx) { return; } int wordIdx = beginIdx; BrailleWord brWord; string text; while (wordIdx < brLine.WordCount) { brWord = brLine[wordIdx]; text = brWord.Text; if (text.Length > 0 && Char.IsDigit(text[0]) && brWord.Cells[0].Value == (byte)BrailleCellCode.Digit) { brWord.Cells.RemoveAt(0); // 移除小數點位. } wordIdx++; } }
/// <summary> /// 處理包著數字的中括號,例如:【12】。 /// </summary> /// <param name="brLine"></param> public static void ApplyBracketRule(BrailleLine brLine) { int wordIdx = 0; BrailleWord brWord; string text; int beginIdx = -1; int endIdx = -1; while (wordIdx < brLine.WordCount) { brWord = brLine[wordIdx]; text = brWord.Text; // 判斷是否為'【' if ("【".Equals(text)) { beginIdx = wordIdx; } else if ("】".Equals(text)) { if (beginIdx >= 0) { endIdx = wordIdx; RemoveDigitCell(brLine, beginIdx + 1, endIdx - 1); } } wordIdx++; } }
/// <summary> /// 編排指定的列。此函式會將指定的列斷行。 /// </summary> /// <param name="brDoc">點字文件。</param> /// <param name="lineIndex">欲重新編排的列索引。</param> /// <returns>傳回編排後的列數。</returns> public int FormatLine(BrailleDocument brDoc, int lineIndex, ContextTagManager context) { BrailleLine brLine = brDoc.Lines[lineIndex]; RemoveContextTagsButTitle(brLine); // 清除情境標籤,除了標題標籤。 if (brLine.WordCount == 0) { brDoc.RemoveLine(lineIndex); return(0); } List <BrailleLine> newLines; newLines = BreakLine(brLine, brDoc.CellsPerLine, context); if (newLines == null) // 沒有斷行? { return(1); } // 移除原始的 line brLine.Clear(); brDoc.RemoveLine(lineIndex); // 加入斷行後的 lines brDoc.Lines.InsertRange(lineIndex, newLines); return(newLines.Count); }
/// <summary> /// 將指定的點字列附加至此點字列。 /// </summary> /// <param name="brLine"></param> public void Append(BrailleLine brLine) { if (brLine == null || brLine.WordCount < 1) { return; } m_Words.AddRange(brLine.Words); }
private void ProcessLine(int lineNumber, string line) { BrailleLine brLine = m_Processor.ConvertLine(lineNumber, line); if (brLine != null) { AddLine(brLine); } }
/// <summary> /// 在指定位置的左邊及右邊各加一個空方。若該位置已經有空方,則不做任何處理。 /// </summary> /// <param name="brLine"></param> /// <param name="index"></param> /// <returns>這次調整一共增加或刪除了幾個 word。</returns> private static int EncloseBlankCells(BrailleLine brLine, int index) { int wordOffset = 0; // NOTE: 一定要先加後面的空方,再插入前面的空方,否則 index 參數必須調整。 wordOffset += PostfixBlankCell(brLine, index); wordOffset += PrefixBlankCell(brLine, index); return(wordOffset); }
/// <summary> /// 根據起始列索引更新起始的 BrailleLine 物件。 /// </summary> /// <param name="brDoc"></param> /// <returns></returns> public bool UpdateLineObject(BrailleDocument brDoc) { if (m_BeginLineIndex < 0 || m_BeginLineIndex >= brDoc.LineCount) { return(false); } m_BeginLine = brDoc.Lines[m_BeginLineIndex]; return(true); }
/// <summary> /// 在指定的位置左邊附加一個空方,若該位置已經有空方,則不做任何處理。 /// </summary> /// <param name="brLine"></param> /// <param name="index"></param> /// <returns>這次調整一共增加或刪除了幾個 word。</returns> private static int PrefixBlankCell(BrailleLine brLine, int index) { int wordOffset = 0; if (index > 0 && !BrailleWord.IsBlank(brLine[index - 1])) { brLine.Words.Insert(index, BrailleWord.NewBlank()); wordOffset = 1; } return(wordOffset); }
/// <summary> /// 深層複製。 /// </summary> /// <returns></returns> public object Clone() { BrailleLine brLine = new BrailleLine(); BrailleWord newWord = null; foreach (BrailleWord brWord in m_Words) { newWord = brWord.Copy(); brLine.Words.Add(newWord); } return(brLine); }
/// <summary> /// 將指定的列與下一列相結合(下一列附加至本列)。 /// </summary> /// <param name="brDoc">點字文件。</param> /// <param name="lineIndex">本列的列索引。</param> public void JoinNextLine(BrailleDocument brDoc, int lineIndex) { BrailleLine brLine = brDoc.Lines[lineIndex]; // 將下一列附加至本列,以結合成一列。 int nextIndex = lineIndex + 1; if (nextIndex < brDoc.Lines.Count) { brLine.Append(brDoc.Lines[nextIndex]); brDoc.Lines.RemoveAt(nextIndex); } }
/// <summary> /// 移除所有情境標籤,除了標題標籤。 /// </summary> public void RemoveContextTagsButTitle(BrailleLine brLine) { BrailleWord brWord; for (int i = brLine.WordCount - 1; i >= 0; i--) { brWord = brLine.Words[i]; if (brWord.IsContextTag && !ContextTag.IsTitleTag(brWord.Text)) { brLine.Words.RemoveAt(i); } } }
/// <summary> /// 在指定的位置右邊附加一個空方,若該位置已經有空方,則不做任何處理。 /// </summary> /// <param name="brLine"></param> /// <param name="index"></param> /// <returns>這次調整一共增加或刪除了幾個 word。</returns> private static int PostfixBlankCell(BrailleLine brLine, int index) { int wordOffset = 0; index++; if (index < brLine.WordCount) // 如果已經到結尾,就不加空方。 { if (!BrailleWord.IsBlank(brLine[index])) { brLine.Words.Insert(index, BrailleWord.NewBlank()); wordOffset = 1; } } return(wordOffset); }
/// <summary> /// 從指定的起始位置複製指定個數的點字 (BrailleWord) 到新建立的點字串列。 /// </summary> /// <param name="index">起始位置</param> /// <param name="count">要複製幾個點字。</param> /// <returns>新的點字串列。</returns> public BrailleLine Copy(int index, int count) { BrailleLine brLine = new BrailleLine(); BrailleWord newWord = null; while (index < m_Words.Count && count > 0) { //newWord = m_Words[index].Copy(); newWord = m_Words[index]; brLine.Words.Add(newWord); index++; count--; } return(brLine); }
/// <summary> /// 根據中文點字的冒號規則修正傳入的點字行。 /// 規則:冒號之後若是 "我" 字,必須加一空方。 /// </summary> /// <param name="brLine"></param> /// <param name="index"></param> /// <returns>這次調整一共增加或刪除了幾個 word。</returns> private static int ApplyColonRule(BrailleLine brLine, int index) { int wordOffset = 0; index++; if (index < brLine.WordCount) { // 若下一個點字是我,則加一空方。 if (brLine[index].Text == "我") { brLine.Words.Insert(index, BrailleWord.NewBlank()); wordOffset = 1; // 跳過空方 } } return(wordOffset); }
public void SetTitleLine(BrailleDocument brDoc, int index) { m_TitleLine = brDoc.Lines[index]; m_TitleLine.RemoveContextTags(); // 移除所有情境標籤(這裡主要是把標題標籤拿掉)。 m_BeginLineIndex = index + 1; // 從下一列開始就是使用此標題。 if (m_BeginLineIndex >= brDoc.LineCount) // 標題列就是文件的最後一列? { //System.Diagnostics.Trace.WriteLine("BraillePageTitle.SetTitleLine: 標題列後面沒有文字內容!"); m_BeginLineIndex = -1; m_BeginLine = null; return; } m_BeginLine = brDoc.Lines[m_BeginLineIndex]; }
/// <summary> /// 更新頁標題的起始列索引。 /// </summary> /// <param name="brDoc"></param> /// <returns></returns> public bool UpdateLineIndex(BrailleDocument brDoc) { if (m_BeginLine == null) { return(false); } int idx = brDoc.Lines.IndexOf(m_BeginLine); if (idx < 0) { return(false); } m_BeginLine = brDoc.Lines[idx]; m_BeginLineIndex = idx; return(true); }
/// <summary> /// 補加必要的空白:在英數字母和中文字之間補上空白。 /// </summary> /// <param name="brLine"></param> public static void AddSpaces(BrailleLine brLine) { int wordIdx = 0; BrailleWord brWord; BrailleWord lastBrWord; int wordOffset; while (wordIdx < brLine.WordCount) { brWord = brLine[wordIdx]; if (brWord.Text.Length < 1) { wordIdx++; continue; } if (Char.IsWhiteSpace(brWord.Text[0])) { wordIdx++; continue; } if (wordIdx == 0) { wordIdx++; continue; } wordOffset = AddBlankForSpecialCharacters(brLine, wordIdx); if (wordOffset > 0) { wordIdx += wordOffset; continue; } // 取前一個字元。 lastBrWord = brLine[wordIdx - 1]; if (NeedSpace(lastBrWord, brWord)) { brLine.Words.Insert(wordIdx, BrailleWord.NewBlank()); wordIdx++; } wordIdx++; } }
/// <summary> /// 處理縮排情境標籤:碰到縮排標籤時,將縮排次數更新至 ContextTagManager 物件,並移除此縮排標籤。 /// /// NOTE: 縮排標籤必須位於列首,一列可有多個連續縮排標籤,例如:<縮排><縮排>。 /// </summary> /// <param name="brDoc"></param> /// <param name="lineIndex"></param> /// <param name="context">情境物件。</param> /// <returns></returns> public void ProcessIndentTags(BrailleDocument brDoc, int lineIndex, ContextTagManager context) { BrailleLine brLine = brDoc.Lines[lineIndex]; int wordIdx = 0; ContextTag ctag; while (brLine.WordCount > 0) { ctag = context.Parse(brLine[0].Text, ContextTagNames.Indent); if (ctag != null) { brLine.RemoveAt(wordIdx); } else { break; } } }
/// <summary> /// 把編號的數字修正成上位點。 /// 注意:此函式會把點字串列中的 # 點字物件刪除。 /// </summary> /// <param name="brLine"></param> public static void FixNumbers(BrailleLine brLine, EnglishBrailleTable brTable) { BrailleWord brWord; bool isNumberMode = false; string brCode; int index = 0; while (index < brLine.WordCount) { brWord = brLine[index]; if (brWord.Text == "#") { isNumberMode = true; brLine.Words.RemoveAt(index); continue; } if (Char.IsDigit(brWord.Text[0])) { if (isNumberMode) { // 把編號的數字改成上位點。 brCode = brTable.FindDigit(brWord.Text, true); if (brWord.Cells.Count > 1) // 第 0 個 cell 可能是數字記號。 { brWord.Cells[1] = BrailleCell.GetInstance(brCode); } else { brWord.Cells[0] = BrailleCell.GetInstance(brCode); } } } else { if (isNumberMode && brWord.Text != "." && brWord.Text != "-" && brWord.Text != ",") { isNumberMode = false; } } index++; } }
/// <summary> /// 套用問號、驚嘆號的點字規則。 /// 規則:後面要加一空方,若後面跟著下引號('」'),則不加空方。 /// </summary> /// <param name="brLine"></param> /// <param name="index"></param> /// <returns></returns> private static int ApplyQuestionRule(BrailleLine brLine, int index) { index++; if (index >= brLine.WordCount) // 如果已經到結尾或超過,就不加空方。 { return(0); } int wordOffset = 0; BrailleWord brWord = brLine[index]; if (!BrailleWord.IsBlank(brWord)) // 若原本已有空方,就不再多加。 { if (!brWord.Text.Equals("」")) { brLine.Words.Insert(index, BrailleWord.NewBlank()); wordOffset = 1; } } return(wordOffset); }
/// <summary> /// 處理那些需要在左右兩邊加空方的字元。 /// </summary> /// <param name="text"></param> /// <returns></returns> private static int AddBlankForSpecialCharacters(BrailleLine brLine, int wordIdx) { int wordOffset = 0; BrailleWord currWord = brLine[wordIdx]; // 目前的字 BrailleWord prevWord = null; // 上一個字 BrailleWord nextWord = null; // 下一個字 if ((wordIdx - 1) >= 0) { prevWord = brLine[wordIdx - 1]; } if ((wordIdx + 1) < brLine.WordCount) { nextWord = brLine[wordIdx + 1]; } switch (brLine[wordIdx].Text) { case "=": wordOffset += EncloseBlankCells(brLine, wordIdx); break; // 注意!! '/' 符號不可以自動加空方,因為在表示分數時,'/' 不加空方。其他情況請編輯時自行加空方。 // case "/": // wordOffset += EncloseBlankCells(brLine, wordIdx); // break; case "%": wordOffset += PostfixBlankCell(brLine, wordIdx); if (prevWord != null && !Char.IsDigit(prevWord.Text[0])) { // % 符號的左邊如果不是數字,則加一空方。 wordOffset += PrefixBlankCell(brLine, wordIdx); } break; default: break; } return(wordOffset); }
/// <summary> /// 把多個連續空白刪到只剩一個。 /// </summary> /// <param name="brLine"></param> public static void ShrinkSpaces(BrailleLine brLine) { int i = 0; int firstSpaceIndex = -1; int spaceCount = 0; BrailleWord brWord; while (i < brLine.WordCount) { brWord = brLine[i]; if (brWord.Text == " ") { spaceCount++; if (firstSpaceIndex < 0) { firstSpaceIndex = i; } } else { // 不是全形空白,把之前取得的全形空白數量刪到剩一個。 if (firstSpaceIndex >= 0 && spaceCount > 1) { int cnt = spaceCount - 1; brLine.Words.RemoveRange(firstSpaceIndex, cnt); i = i - cnt; } firstSpaceIndex = -1; spaceCount = 0; } i++; } // 去掉最後的連續空白 if (firstSpaceIndex >= 0 && spaceCount > 1) { int cnt = spaceCount - 1; brLine.Words.RemoveRange(firstSpaceIndex, cnt); } }
/// <summary> /// 套用句號規則: /// 1.在同一點字行中,句號之後,須空一方,再點寫下文; /// 如在行末不夠點寫句號時,須將原句最末一字與句號移至次一行連書, /// 然後空方接寫下文;如句號恰在一行之最末一方,下文換行點寫時,次行之首無須空方。 /// 2.句號可與後引號(包括後單或雙引號)、刪節號、後括弧 /// (包括後圓括弧、後方括弧、後大括弧)、後夾註號連書,但不得單獨書於一行之首)。 /// </summary> /// <param name="brLine"></param> /// <param name="index"></param> /// <returns></returns> private static int ApplyPeriodRule(BrailleLine brLine, int index) { index++; if (index >= brLine.WordCount) // 如果已經到結尾或超過,就不加空方。 { return(0); } int wordOffset = 0; BrailleWord brWord = brLine[index]; if (!BrailleWord.IsBlank(brWord)) // 若原本已有空方,就不再多加。 { // 句號可與標點符號連書而無須加空方。 if (!BrailleWord.IsChinesePunctuation(brWord)) { brLine.Words.Insert(index, BrailleWord.NewBlank()); wordOffset = 1; } } return(wordOffset); }
/// <summary> /// 在指定的索引處的 BrailleWord 物件中加入數符。 /// 此函式會自動判斷是否有不需加入數符的例外狀況,例如:次方。 /// </summary> /// <param name="brLine"></param> /// <param name="index"></param> private static void AddDigitSymbol(BrailleLine brLine, int index) { BrailleCell digitCell = BrailleCell.GetInstance(BrailleCellCode.Digit); bool needDigitSymbol = true; if (index > 0) // 先檢查前一個字元,是否為不需加數符的特例。 { if (brLine.Words[index - 1].Text.Equals("^")) // 次方。 { needDigitSymbol = false; } } if (needDigitSymbol) { BrailleWord firstDigitWord = brLine.Words[index]; // 如果已經有加上數字記號就不再重複加。 if (!firstDigitWord.Cells[0].Equals(digitCell)) { firstDigitWord.Cells.Insert(0, digitCell); } } }
/// <summary> /// 移除指定的列。注意:只做移除列的動作,不可清除列的內容物件!! /// </summary> /// <param name="index"></param> public void RemoveLine(int index) { BrailleLine brLine = m_Lines[index]; m_Lines.RemoveAt(index); }
/// <summary> /// 加入一列。 /// </summary> /// <param name="brLine"></param> public void AddLine(BrailleLine brLine) { m_Lines.Add(brLine); }
/// <summary> /// 計算斷行位置。 /// </summary> /// <param name="brLine">點字串列。</param> /// <param name="cellsPerLine">每行最大允許幾方。</param> /// <param name="needHyphen">是否在斷行處附加一個連字號 '-'。</param> /// <returns>傳回可斷行的點字索引。</returns> private static int CalcBreakPoint(BrailleLine brLine, int cellsPerLine, out bool needHyphen) { needHyphen = false; // 先根據每列最大方數取得要斷行的字元索引。 int fixedBreakIndex = brLine.CalcBreakPoint(cellsPerLine); if (fixedBreakIndex >= brLine.WordCount) // 無需斷行? { return(fixedBreakIndex); } // 需斷行,根據點字規則調整斷行位置。 int breakIndex = fixedBreakIndex; BrailleWord breakWord; // 必須和前一個字元一起斷至下一行的字元。亦即,只要剛好斷在這些字元,就要改成斷前一個字元。 char[] joinLeftChars = { ',', '.', '。', '、', ',', ';', '?', '!', '」', '』', '‧' }; int loopCount = 0; while (breakIndex >= 0) { loopCount++; if (loopCount > 10000) { throw new Exception("偵測到無窮回圈於 BrailleProcessor.CalcBreakPoint(),請通知程式設計師!"); } breakWord = brLine[breakIndex]; if (breakWord.DontBreakLineHere) // 如果之前已經設定這個字不能在此處斷行 { breakIndex--; continue; } if (breakWord.Text.IndexOfAny(joinLeftChars) >= 0) { // 前一個字要和此字元一起移到下一行。 breakIndex--; continue; // 繼續判斷前一個字元可否斷行。 } if (breakWord.IsWhiteSpace) // 找到空白處,可斷開 { breakIndex++; // 斷在空白右邊的字元。 break; } // 處理數字的斷字:連續數字不可斷開。 if (breakWord.IsDigit) { breakIndex--; while (breakIndex >= 0) { if (!brLine[breakIndex].IsDigit) { break; } breakIndex--; } } else if (breakWord.IsLetter) // 英文單字不斷字。 { breakIndex--; while (breakIndex >= 0) { if (!brLine[breakIndex].IsLetter) { break; } breakIndex--; } } else if (breakWord.Text.Equals("_")) // 連續底線不斷字。 { breakIndex--; while (breakIndex >= 0) { if (!brLine[breakIndex].Text.Equals("_")) { break; } breakIndex--; } } else { break; } } // of while (breakIndex >= 0) if (breakIndex <= 0) { // 若此處 breakIndex < 0,表示找不到任何可斷行的位置; // 若此處 breakIndex == 0,表示可斷在第一個字元,那也沒有意義,因此也視為找不到斷行位置。 //Trace.WriteLine("無法找到適當的斷行位置,使用每列最大方數斷行!"); breakIndex = fixedBreakIndex; } // 注意!! 若 breakIndex 傳回 0 會導致呼叫的函式進入無窮迴圈!! return(breakIndex); }
/// <summary> /// 把一行明眼字串轉換成點字串列。 /// 此時不考慮一行幾方和斷行的問題,只進行單純的轉換。 /// 斷行由其他函式負責處理,因為有些點字規則必須在斷行時才能處理。 /// </summary> /// <param name="lineNumber">字串的行號。此參數只是用來當轉換失敗時,傳給轉換失敗事件處理常式的資訊。</param> /// <param name="line">輸入的明眼字串。</param> /// <param name="isTitle">輸出參數,是否為標題。</param> /// <returns>點字串列。若則傳回 null,表示該列不需要轉成點字。</returns> public BrailleLine ConvertLine(int lineNumber, string line) { if (line == null) { return(null); } BrailleLine brLine = new BrailleLine(); string orgLine = line; // 保存原始的字串。 // 把換行符號之後的字串去掉 int i = line.IndexOfAny(new char[] { '\r', '\n' }); if (i >= 0) { line = line.Substring(0, i); } // 若去掉換行字元之後變成空字串,則傳回只包含一個空方的列。 if (String.IsNullOrEmpty(line)) { brLine.Words.Add(BrailleWord.NewBlank()); return(brLine); } // 預先處理特殊標籤的字元替換。 line = PreprocessTagsForLine(line); if (line == null) { return(null); } // 如果是原書頁碼,先檢查格式是否正確。 try { GetOrgPageNumber(line); } catch (Exception ex) { m_ErrorMsg.Append(String.Format("第 {0} 列 : ", lineNumber)); m_ErrorMsg.Append(ex.Message); m_ErrorMsg.Append("\r\n"); return(null); } line = StrHelper.Reverse(line); Stack <char> charStack = new Stack <char>(line); char ch; List <BrailleWord> brWordList; StringBuilder text = new StringBuilder(); ConvertionFailedEventArgs cvtFailedArgs = new ConvertionFailedEventArgs(); TextConvertedEventArgs textCvtArgs = new TextConvertedEventArgs(); while (charStack.Count > 0) { brWordList = ConvertWord(charStack); if (brWordList != null && brWordList.Count > 0) { // 成功轉換成點字,有 n 個字元會從串流中取出 brLine.Words.AddRange(brWordList); text.Length = 0; foreach (BrailleWord brWord in brWordList) { text.Append(brWord.Text); } textCvtArgs.SetArgValues(lineNumber, text.ToString()); OnTextConverted(textCvtArgs); } else { // 無法判斷和處理的字元應該會留存在串流中,將之取出。 ch = charStack.Pop(); int charIndex = line.Length - charStack.Count; // 引發事件。 cvtFailedArgs.SetArgs(lineNumber, charIndex, orgLine, ch); OnConvertionFailed(cvtFailedArgs); if (cvtFailedArgs.Stop) { break; } } // 如果進入分數情境,就把整個分數處理完。 if (m_ContextTagManager.IsActive(ContextTagNames.Fraction)) { try { brWordList = ConvertFraction(lineNumber, charStack); if (brWordList != null && brWordList.Count > 0) { // 成功轉換成點字,有 n 個字元會從串流中取出 brLine.Words.AddRange(brWordList); } } catch (Exception ex) { m_ErrorMsg.Append(String.Format("第 {0} 列 : ", lineNumber)); m_ErrorMsg.Append(ex.Message); m_ErrorMsg.Append("\r\n"); } } } ChineseBrailleRule.ApplyPunctuationRules(brLine); // 套用中文標點符號規則。 // 不刪除多餘空白,因為原本輸入時可能就希望縮排。 //ChineseBrailleRule.ShrinkSpaces(brLine); // 把連續全形空白刪到只剩一個。 // 將編號的數字修正成上位點。 if (m_EnglishConverter != null) { EnglishBrailleRule.FixNumbers(brLine, m_EnglishConverter.BrailleTable as EnglishBrailleTable); } EnglishBrailleRule.ApplyCapitalRule(brLine); // 套用大寫規則。 EnglishBrailleRule.ApplyDigitRule(brLine); // 套用數字規則。 EnglishBrailleRule.AddSpaces(brLine); // 補加必要的空白。 ChineseBrailleRule.ApplyBracketRule(brLine); // 套用括弧規則。 // 不刪除多於空白,因為原本輸入時可能就希望縮排。 //EnglishBrailleRule.ShrinkSpaces(brLine); // 把連續空白刪到只剩一個。 return(brLine); }