public void Insert(int index, BrailleWord brWord) { m_Words.Insert(index, brWord); }
private bool m_IsEngPhonetic; // 是否為英語音標(用來判斷不要加空方). //private bool m_QuotationResolved; // 是否已經識別出左右引號(英文的單引號和雙引號都是同一個符號,但點字不同) static BrailleWord() { m_Blank = BrailleWord.NewBlank(); }
/// <summary> /// 轉換分數。 /// </summary> /// <param name="lineNumber"></param> /// <param name="chars"></param> /// <returns></returns> private List <BrailleWord> ConvertFraction(int lineNumber, Stack <char> chars) { char[] charAry = chars.ToArray(); string s = new string(charAry); int idxEof = s.IndexOf(ContextTag.GetEndTagName(ContextTagNames.Fraction)); // end of fraction if (idxEof < 0) { throw new Exception("<分數> 標籤有起始但沒有結束標籤!"); } s = s.Substring(0, idxEof); // 從字串頭取到 </分數> 標籤之前。 string intPart; string numerator; string denumerator; ParseFraction(s, out intPart, out numerator, out denumerator); // Note: 整數部份、分子、分母都有可能是英文字母(代數)。 string temp; Stack <char> charStack; List <BrailleWord> brWordList = null; List <BrailleWord> brWordListIntPart = new List <BrailleWord>(); if (!String.IsNullOrEmpty(intPart)) { // 將整數部份轉換成點字串列。 temp = StrHelper.Reverse(intPart); charStack = new Stack <char>(temp); while (charStack.Count > 0) { brWordList = ConvertWord(charStack); if (brWordList == null) { throw new Exception("無法轉換分數的整數部份!"); } else { brWordListIntPart.AddRange(brWordList); } } } // 將分子部份轉換成點字串列。 temp = StrHelper.Reverse(numerator + "/"); charStack = new Stack <char>(temp); List <BrailleWord> brWordListNumerator = new List <BrailleWord>(); while (charStack.Count > 0) { brWordList = ConvertWord(charStack); if (brWordList == null) { throw new Exception("無法轉換分數的分子部份!"); } else { brWordListNumerator.AddRange(brWordList); } } // 分子的數字不要加數符 foreach (BrailleWord brWord in brWordListNumerator) { brWord.NoDigitCell = true; } // 補上分子的點字符號 brWordListNumerator[0].Cells.Insert(0, BrailleCell.GetInstance(new int[] { 1, 4, 5, 6 })); if (brWordListIntPart.Count > 0) { brWordListNumerator[0].Cells.Insert(0, BrailleCell.GetInstance(new int[] { 4, 5, 6 })); } // 將分母部份轉換成點字串列。 temp = StrHelper.Reverse(denumerator); charStack = new Stack <char>(temp); List <BrailleWord> brWordListDenumerator = new List <BrailleWord>(); while (charStack.Count > 0) { brWordList = ConvertWord(charStack); if (brWordList == null) { throw new Exception("無法轉換分數的分母部份!"); } else { brWordListDenumerator.AddRange(brWordList); } } // 分母的數字不要加數符 foreach (BrailleWord brWord in brWordListDenumerator) { brWord.NoDigitCell = true; } // 補上分母後面的點字符號 BrailleWord lastBrWord = brWordListDenumerator[brWordListDenumerator.Count - 1]; if (brWordListIntPart.Count > 0) { lastBrWord.Cells.Add(BrailleCell.GetInstance(new int[] { 4, 5, 6 })); } lastBrWord.Cells.Add(BrailleCell.GetInstance(new int[] { 3, 4, 5, 6 })); // 結合整數部份、分子、分母至同一個串列。 List <BrailleWord> brWordListFraction = new List <BrailleWord>(); brWordListFraction.AddRange(brWordListIntPart); brWordListFraction.AddRange(brWordListNumerator); brWordListFraction.AddRange(brWordListDenumerator); // 完成! 從傳入的字元堆疊中取出已經處理的字元。 while (idxEof > 0) { chars.Pop(); idxEof--; } return(brWordListFraction); }
/// <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); }
/// <summary> /// 將一行點字串列斷成多行。 /// </summary> /// <param name="brLine">來源點字串列。</param> /// <param name="cellsPerLine">每行最大方數。</param> /// <param name="context">情境物件。</param> /// <returns>斷行之後的多行串列。若為 null 表示無需斷行(指定的點字串列未超過每行最大方數)。</returns> public List <BrailleLine> BreakLine(BrailleLine brLine, int cellsPerLine, ContextTagManager context) { if (context != null && context.IndentCount > 0) // 若目前位於縮排區塊中 { // 每列最大方數要扣掉縮排數量,並於事後補縮排的空方。 // NOTE: 必須在斷行之後才補縮排的空方! cellsPerLine -= context.IndentCount; } // 若指定的點字串列未超過每行最大方數,則無須斷行,傳回 null。 if (brLine.CellCount <= cellsPerLine) { // 補縮排的空方。 if (context != null && context.IndentCount > 0) // 若目前位於縮排區塊中 { this.Indent(brLine, context.IndentCount); } return(null); } List <BrailleLine> lines = new List <BrailleLine>(); BrailleLine newLine = null; int wordIndex = 0; int breakIndex = 0; bool needHyphen = false; bool isBroken = false; // 是否已經斷行了? int indents = 0; // 第一次斷行時,不會有系統自斷加上的縮排,因此初始為 0。 int maxCells = cellsPerLine;; // 計算折行之後的縮排格數。 indents = BrailleProcessor.CalcNewLineIndents(brLine); while (wordIndex < brLine.WordCount) { breakIndex = BrailleProcessor.CalcBreakPoint(brLine, maxCells, out needHyphen); newLine = brLine.Copy(wordIndex, breakIndex); // 複製到新行。 if (needHyphen) // 是否要附加連字號? { newLine.Words.Add(new BrailleWord("-", BrailleCellCode.Hyphen)); } newLine.TrimEnd(); // 去尾空白。 // 如果是折下來的新行,就自動補上需要縮排的格數。 if (isBroken) { for (int i = 0; i < indents; i++) { newLine.Insert(0, BrailleWord.NewBlank()); } } brLine.RemoveRange(0, breakIndex); // 從原始串列中刪除掉已經複製到新行的點字。 wordIndex = 0; lines.Add(newLine); // 防錯:檢驗每個斷行後的 line 的方數是否超過每列最大方數。 // 若超過,即表示之前的斷行處理有問題,須立即停止執行,否則錯誤會 // 直到在雙視編輯的 Grid 顯示時才出現 index out of range,不易抓錯! System.Diagnostics.Debug.Assert(newLine.CellCount <= cellsPerLine, "斷行錯誤! 超過每列最大方數!"); // 被折行之後的第一個字需要再根據規則調整。 EnglishBrailleRule.ApplyCapitalRule(brLine); // 套用大寫規則。 EnglishBrailleRule.ApplyDigitRule(brLine); // 套用數字規則。 isBroken = true; // 已經至少折了一行 maxCells = cellsPerLine - indents; // 下一行開始就要自動縮排,共縮 indents 格。 } // 補縮排的空方。 if (context != null && context.IndentCount > 0) // 若目前位於縮排區塊中 { indents = context.IndentCount; foreach (BrailleLine aLine in lines) { this.Indent(aLine, indents); } } return(lines); }
/// <summary> /// 根據前後鄰近的字元判斷中間是否需要加一個空方。 /// </summary> /// <param name="lastWord">前一個字。</param> /// <param name="currWord">目前的字。</param> /// <returns></returns> private static bool NeedSpace(BrailleWord lastWord, BrailleWord currWord) { if (lastWord == null || currWord == null) { throw new ArgumentException("傳入 NeedSpace() 的參數為 null!"); } if (lastWord.IsEngPhonetic && currWord.IsEngPhonetic) { return(false); } if (String.IsNullOrEmpty(lastWord.Text) || String.IsNullOrEmpty(currWord.Text)) { return(false); } if (ContextTag.StartsWithContextTag(lastWord.Text)) // 如果前一個字是情境標籤,就不加空方 { return(false); } if (ContextTag.StartsWithContextTag(currWord.Text)) // 如果目前的字是情境標籤,就不加空方 { return(false); } char lastChar = lastWord.Text[0]; char currChar = currWord.Text[0]; // 若前一個字元已經是空白,就不用處理。 if (Char.IsWhiteSpace(lastChar)) { return(false); } if (lastChar == '★') // 「點譯者註」的後面不加空方。 { return(false); } if (lastChar == '□' || currChar == '□') // 「滿點」符號的左右均不加空方。 { return(false); } if (CharHelper.IsAscii(currChar) && !CharHelper.IsAscii(lastChar)) { // 目前的字元是 ASCII,但前一個字元不是(視為中文或其他雙位元組字元),需插入 // 一個空白,除了一些例外,如:全型+號前後若接數字,即視為數學式子,+號前後都不空方。 if (Char.IsDigit(currChar)) { switch (lastChar) { case '+': case '-': case '×': // 全型乘號 (VS2005 編輯器無法正確顯示) case '÷': // 全型除號 (VS2005 編輯器無法正確顯示) case '(': case '【': // // 用粗中刮弧把數字包起來時,代表題號,不用加空方. return(false); } } return(true); } if (!CharHelper.IsAscii(currChar) && CharHelper.IsAscii(lastChar)) { // 目前的字元不是 ASCII,但前一個字元是,需插入一個空白, // 除了一些例外,例如:12℃ 的溫度符號前面不加空方。 if (Char.IsDigit(lastChar)) { switch (currChar) { case '∘': // 溫度符號 case '℃': case '+': case '-': case '×': // 全型乘號 (VS2005 編輯器無法正確顯示) case '÷': // 全型除號 (VS2005 編輯器無法正確顯示) case ')': case '】': // 用粗中刮弧把數字包起來時,代表題號,不用加空方. return(false); } } return(true); } return(false); }
/// <summary> /// 根據標點符號規則調整一整行點字。 /// </summary> /// <param name="brLine"></param> public static void ApplyPunctuationRules(BrailleLine brLine) { int wordIdx = 0; BrailleWord brWord; string text; while (wordIdx < brLine.WordCount) { brWord = brLine[wordIdx]; if (brWord.Language != BrailleLanguage.Chinese) { wordIdx++; continue; } text = brWord.Text; if (text.Length == 3) { // 判斷是否為特殊的編號(選擇題的答案編號) if (text[0] == '【' && Char.IsDigit(text[1]) && text[2] == '】') { // 前後需各加一空方。 //wordIdx += EncloseBlankCells(brLine, wordIdx) + 1; continue; } } // 處理緊鄰的"()",在中間加一空方,使其變成"( )"。 if ("(".Equals(text) && ((wordIdx + 1) < brLine.WordCount)) { BrailleWord brWord2 = brLine[wordIdx + 1]; if (")".Equals(brWord2.Text)) { // 兩個緊鄰的點字:"()",要在左括弧前面加一空方,且括弧中間要加一空方, // 否則組成的點字會跟 '○' 的點字一樣。 int offset = EncloseBlankCells(brLine, wordIdx); // 設定 '(' 後面跟著的空方和 ')' 為不可斷行。 brLine[wordIdx + 1].DontBreakLineHere = true; brLine[wordIdx + 2].DontBreakLineHere = true; wordIdx += offset + 1; continue; } if (BrailleWord.IsBlank(brWord2) && ((wordIdx + 2) < brLine.WordCount)) { BrailleWord brWord3 = brLine[wordIdx + 2]; if (")".Equals(brWord3.Text)) { // 原本輸入的文字就已經在 '(' 和 ')' 中間加了空方。 // 設定 '(' 後面跟著的空方和 ')' 為不可斷行。 brLine[wordIdx + 1].DontBreakLineHere = true; brLine[wordIdx + 2].DontBreakLineHere = true; int offset = PrefixBlankCell(brLine, wordIdx); wordIdx += offset + 2; continue; } } // 註:左括弧後面跟著的既不是空白也不是右括弧,視為括弧中有文字, // 規則為:左括弧前方不空方,括弧中間不空方,右括弧後面要空方。 // 由於無論什麼情況,右括弧後面一定要加空方,因此由下面的程式碼處理, // 這裡無須搜尋對應的右括弧並加空方。 } switch (text) { case "。": case "」": // 下引號的規則同句號。 wordIdx += ApplyPeriodRule(brLine, wordIdx); break; case ",": wordIdx += ApplyCommaRule(brLine, wordIdx); break; case ":": wordIdx += ApplyColonRule(brLine, wordIdx); break; case "!": // 驚嘆號、問號的規則相同。 case "?": wordIdx += ApplyQuestionRule(brLine, wordIdx); break; case ")": // 後面要加一空方。 case "∘": // 溫度符號 case "℃": wordIdx += PostfixBlankCell(brLine, wordIdx); break; case "※": // 前後要加一空方。 case "◎": case "○": case "╳": case "←": case "→": case "↑": case "↓": case "∴": case "∵": case "=": case "≠": case "<": case ">": case "≦": case "≧": case "∠": case "⊥": wordIdx += EncloseBlankCells(brLine, wordIdx); break; case "√": // 打勾符號,前後須加空方,後面接逗號時需特殊處理。 if ((wordIdx + 1) < brLine.WordCount) { string text2 = brLine[wordIdx + 1].Text; if (text2.Equals(",") || text2.Equals(",")) { // 打勾符號後面若跟著逗號,則可連書,且該逗號需用第 6 點。 brLine[wordIdx + 1].CellList.Clear(); brLine[wordIdx + 1].CellList.Add(BrailleCell.GetInstance(new int[] { 6 })); wordIdx += PrefixBlankCell(brLine, wordIdx); } else { // 否則打勾符號前後 wordIdx += EncloseBlankCells(brLine, wordIdx); } } else { wordIdx += EncloseBlankCells(brLine, wordIdx); } break; case "▲": // 刪除符號起始 wordIdx += PrefixBlankCell(brLine, wordIdx); break; case "▼": // 刪除符號結束 wordIdx += PostfixBlankCell(brLine, wordIdx); break; default: break; } wordIdx++; } }