0

How can I get the bounding box for each ruby annotation character (or furigana) in a CTFrame text rendered by CoreText and CoreGraphics in iOS? (iOS 11, 12 or 13)

enter image description here

Based on this question Core Text calculate letter frame in iOS I made a swift version program that encloses each character in a red box but I wasn't able to get the bounding boxes for the ruby annotation characters, aka furigana. I painted in green the ruby character boxes for the purpose of this question.

How can I achieve that? Any Ideas?

Edit: For clarity, the green boxes were painted by hand in an illustration software. I need to draw the green boxes by code.

The full source is in GitHub https://github.com/huse360/LetterFrame This is the main source code:

 override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
   
    
       context.textMatrix = .identity
       context.translateBy(x: 0, y: self.bounds.size.height)
       context.scaleBy(x: 1.0, y: -1.0)
   
    
       let string = "|優勝《ゆうしょう》の|懸《か》かった|試合《しあい》。|Test《テスト》.\nThe quick brown fox jumps over the lazy dog. 12354567890 @#-+"

    
       let attributedString = Utility.sharedInstance.furigana(String: string)
   
       let range = attributedString.mutableString.range(of: attributedString.string)

       attributedString.addAttribute(.font, value: font, range: range)

   
       let framesetter = attributedString.framesetter()
   
       let textBounds = self.bounds.insetBy(dx: 20, dy: 20)
       let frame = framesetter.createFrame(textBounds)
    
//Draw the frame text:
   
       frame.draw(in: context)
           
       let origins = frame.lineOrigins()
   
       let lines = frame.lines()

        context.setStrokeColor(UIColor.red.cgColor)
        context.setLineWidth(0.7)

        for i in 0 ..< origins.count {

            let line = lines[i]
         
            for run in line.glyphRuns() {
            
                let font = run.font
                let glyphPositions = run.glyphPositions()
                let glyphs = run.glyphs()
            
                let glyphsBoundingRects =  font.boundingRects(of: glyphs)
            
//DRAW the bounding box for each glyph:
            
                for k in 0 ..< glyphPositions.count {
                    let point = glyphPositions[k]
                    let gRect = glyphsBoundingRects [k]
            
                    var box = gRect
                    box.origin +=  point + origins[i] + textBounds.origin
                    context.stroke(box)
                        
                }// for k
        
            }//for run
        
       }//for i
    
    }//func draw
huse
  • 324
  • 2
  • 11
  • How do you know that the green bounding boxes are incorrect? I mean, they might look visually incorrect, but in the font itself the glyph might have some padding. – Eugene Dudnyk Aug 13 '20 at 03:42
  • No, the green ones were painted by hand in an illustration software, just for the purpose of this question. The red ones came from the program. I mean: I need to draw the green ones by code. – huse Aug 13 '20 at 03:47
  • You can try to convert those glyphs to `CGPathRef`, and then find the bounding box of `CGPathRef`, like this: `let letter = CTFontCreatePathForGlyph(font, glyphs[k], nil)`, `let gRect = letter.boundingBox` – Eugene Dudnyk Aug 13 '20 at 03:55
  • I did it but the ruby annotation is missing... I can get the ruby text from the CTRun but not the screen coordinates. I appreciate if you can elaborate a bit more... – huse Aug 13 '20 at 04:10
  • Yeah, seems like CTRun does not consider annotations as parts of the displayed glyph. Looks like the exact position and size of annotations is available only on the private API level. – Eugene Dudnyk Aug 13 '20 at 04:54
  • It must be a way to solve this. – huse Aug 13 '20 at 07:46

0 Answers0