月別アーカイブ: 2017年7月

ルビ付きのNSAttributedStringを使うとiOS8でBAD_ACCESSが発生する

原因としてはルビに空文字を設定してるのが悪いっぽい。

たとえばこんな感じ

// 空文字をルビとして設定する
let ruby = ""
var textRef: [Unmanaged<CFString>?] = [Unmanaged<CFString>.passRetained(ruby as CFString) as Unmanaged<CFString>, .none, .none, .none]
let annotation = CTRubyAnnotationCreate(.auto, .auto, 0.5, &textRef[0]!)
                
let attributedString = NSAttributedString(
                    string: "漢字",
                    attributes: [kCTRubyAnnotationAttributeName as String: annotation]

// ↓ ここでEXC_BAD_ACCESSが発生する
let line = CTLineCreateWithAttributedString(attributedString)

 

ほかにも

let framesetter = CTFramesetterCreateWithAttributedString( attributedText)

// ↓ ここでEXC_BAD_ACCESSが発生する
let frame = CTFramesetterCreateFrame(framesetter, CFRange(), path, nil)

ちなみにiOS10じゃ発生しないよ。

Javaで青空文庫のルビをパースする

Swiftで作ったサンプルのJava版です。
仕様などはSwift版と同様なので→Swift版

こちらもJavaで正規表現を利用するサンプルにもなってます。
 

使い方

String sampleText = "てめえらの|血《ち》は|何色《なにいろ》だーっ!!";
AozoraRubyParser parser = new AozoraRubyParser(sampleText);
List<AozoraRubyParser.Result> tokens = parser.parse();
for (AozoraRubyParser.Result r : tokens){
  Log.d("", r.text +  (r.ruby != null ? "[" + r.ruby + "]" : "") );
}

 

パーサーのコード

public class AozoraRubyParser {

  static public class Result {
    public String text;
    public String ruby;

    public Result(String t, String r) {
      this.text = t;
      this.ruby = r;
    }
  }


  private String text;

  public AozoraRubyParser(String text) {
    this.text = text;
  }

  public List<Result> parse() {

    ArrayList<Result> result = new ArrayList<>();

    String patternStr = "|(.+?)《(.+?)》";
    Pattern ptn = Pattern.compile(patternStr);

    int position = 0;

    Matcher matcher = ptn.matcher(text);
    while (matcher.find()) {
      if (position < matcher.start()) {
        String subText = this.text.substring(position, matcher.start());
        result.add(new Result(subText, null));
      }
      position = matcher.end();

      // 文字と読み仮名を抽出
      result.add(new Result(matcher.group(1), matcher.group(2)));
    }

    // 文末の残りを追加
    if (this.text.length() > position) {
      String subText = this.text.substring(position, this.text.length());
      result.add(new Result(subText, null));
    }

    return result;
  }
}

Swiftで青空文庫のルビをパースする

青空文庫のルビ表記をパースするサンプルを作ってみました。

青空文庫のルビはこんな感じで定義されてます。
http://www.aozora.gr.jp/KOSAKU/MANUAL_2.html

本来はリンクの通り、いろいろと細かな仕様があるのですが、今回はプログラムで「ルビ付き文字列を定義する」ということだけを狙ってシンプルな仕様で実装してみます。
具体的には↓のフォーマットだけを対象とします。

|漢字《よみかた》

例えばこんな感じですね。

「てめえらの|血《ち》は|何色《なにいろ》だーっ!!」

先に使い方ですが

let text = "てめえらの|血《ち》は|何色《なにいろ》だーっ!!"
let parser = AozoraRubyParser(text: text)
if let result = parser.parse() {
	for r in result {
		print("\(r.text) \( (r.ruby != nil) ? "[" + r.ruby! + "]" : "" )")
	}
}

こんな感じで出力されます
> てめえらの
> 血 [ち]
> は
> 何色 [なにいろ]
> だーっ!!

class AozoraRubyParser {
  
  class Result {
    let text:String
    let ruby:String?
    init(text:String,ruby:String?) {
      self.text = text
      self.ruby = ruby
    }
  }
  
  
  let text:String;  
  
  init(text:String) {
    self.text = text;
  }
    
  func parse() -> [Result]?{    
    
    var result:[Result] = []
    do {
  
      var position = 0;
      let nsText = text as NSString
      let totalLength = nsText.length
      
      let re = try NSRegularExpression(pattern: "|(.+?)《(.+?)》", options: [])
  
      let matches = re.matches(in: text, options: [], range: NSRange(location: 0, length: totalLength))
      for match in matches {
        let matchRange = match.range;
        
        // 現在の位置からrangeの先頭が離れていたら、その間の文字を抽出する
        if position < matchRange.location {
          let subRange = NSRange(location: position, length: matchRange.location - position)
          result.append(Result(text: nsText.substring(with:subRange), ruby: nil))
        }
        position = matchRange.location + matchRange.length;
	
        // 文字と読み仮名を抽出
        let str = nsText.substring(with: match.rangeAt(1))
        let ruby = nsText.substring(with: match.rangeAt(2))
        result.append(Result(text: str, ruby: ruby))
        
      }
      
      // 文末の残りを追加
      if totalLength > position {
        let subRange = NSRange(location: position, length: totalLength - position)
        result.append(Result(text: nsText.substring(with:subRange), ruby: nil))
      }
      
      
    }catch let err  {      
      log.error("\(err)")
      return nil;
    }
  
    return result
  }

}

Swiftで正規表現を使うサンプルとしてもどうぞ。