Core Text calculate letter frame in iOS

后端 未结 2 1183
误落风尘
误落风尘 2020-12-07 08:33

I need to calculate exact bounding box for every each character (glyph) in NSAttributedString (Core Text). After putting together some code used to solve similar problems (C

相关标签:
2条回答
  • 2020-12-07 08:57

    Swift version

    Swift 5, Xcode 11:

     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
    

    Made with a CoreText Swift Wrapper. Full Source: https://github.com/huse360/LetterFrame

    0 讨论(0)
  • 2020-12-07 09:02

    You did an impressive amount of work in your question and were so close on your own. The problem you were having comes from this line of code where you position the bounding boxes for each frame:

    _characterFrames[ic].origin = CGPointMake(startOffset, lineOrigin.y);
    

    The problem with it is that you are overriding whatever offset the frame already had.

    If you were to comment out that line you would see that all the frames were positioned more or less in the same place but you would also see that they are not positioned at the exact same place. Some are positioned more to the left or right and some more up or down. This means that the frames for the glyphs have a position of their own.

    enter image description here

    The solution to your problem is to take the current position of the frames into account when you move them into their correct place on the lines. You can either do it by adding to x and y separately:

    _characterFrames[ic].origin.x += startOffset;
    _characterFrames[ic].origin.y += lineOrigin.y;
    

    or by offsetting the rectangle:

    _characterFrames[ic] = CGRectOffset(_characterFrames[ic],
                                        startOffset, lineOrigin.y);
    

    Now the bounding boxes will have their correct positions:

    enter image description here

    and you should see that it works for some of the more extreme fonts out there

    enter image description here

    0 讨论(0)
提交回复
热议问题