среда, 30 октября 2013 г.

"Голый код". Пример использования CoreText

- (void) render: (EVDRenderContext &) aCtx : (DoRenderedPara) aDoRendered : (DoPageEnded) aPageEnded
{
    EVDRenderedPara *l_Para = nil;
    @try {
        CFAttributedStringRef l_Text = self.Text;
        
        int vTextLength = 0;
        if (l_Text)
            vTextLength = CFAttributedStringGetLength(l_Text);
        
        if (vTextLength <= 0) {
            // - пустые строки надо как-то отдельно обрабатывать
            //aOffsetInParaRenderingNow = 0;
            if (aCtx.rParaAdded)
                aCtx.rRect.size.height -= 10;
            l_Para = [[EVDRenderedPara alloc] initWithFrame: nil andPara: self];
            aDoRendered(l_Para);
        }
        else {
            CTFramesetterRef l_Framesetter = CTFramesetterCreateWithAttributedString(l_Text);
            @try {
#ifdef UseHoles
                CGMutablePathRef l_Path = CGPathCreateMutable();
                CGPathAddRect(l_Path, NULL, aCtx.rRect);
                
                CGFloat vHeight = 30;
                vHeight = MIN(vHeight, aCtx.rRect.size.height - 10);
                vHeight = MAX(0, vHeight);
//                CGFloat vDelta = 20;
                CGFloat vDelta = 0;
                vDelta = -vDelta;
                
                {
//                    CGFloat vIndent = 40;
                    CGFloat vIndent = 0;
//                    CGFloat vWidth = 50;
                    CGFloat vWidth = 80;
                    CGRect vHole;
                    vHole.origin.x = aCtx.rRect.origin.x + vIndent;
                    vHole.origin.y = aCtx.rRect.size.height - vHeight + vDelta;
                    vHole.size.width = vWidth;
                    vHole.size.height = vHeight + 10/* - vDelta*/;
                    if (vHole.origin.y >= 0) {
//                        CGPathAddEllipseInRect(l_Path, NULL, vHole);
                        CGPathAddRect(l_Path, NULL, vHole);
                    }
                }
#else
                CGPathRef l_Path = CGPathCreateWithRect(aCtx.rRect, NULL);
#endif // UseHoles
                @try {
                    //        CGMutablePathRef l_Path = CGPathCreateMutable();
                    //        CGPathAddRect(l_Path, NULL, aRect);
                    
                    CTFrameRef l_Frame = CTFramesetterCreateFrame(l_Framesetter,
                                                                  CFRangeMake(aCtx.rCursor.rOffset, 0), l_Path, NULL);
                    @try {
                        l_Para = [[EVDRenderedPara alloc] initWithFrame: l_Frame andPara: self];
                    }
                    @finally {
                        if (l_Frame)
                            CFRelease(l_Frame);
                    }
                }
                @finally {
                    CFRelease(l_Path);
                }
            }
            @finally {
                CFRelease(l_Framesetter);
                l_Framesetter = NULL;
            }
            aDoRendered(l_Para);
            aCtx.rParaAdded = YES;
            if ([l_Para CheckPageIsEnded: vTextLength : aCtx.rRect : aCtx.rCursor]) {
                // - тут закончилась страница
                aPageEnded();
            }
        }
    }
    @finally {
        DESTROY(l_Para);
    }
    aCtx.rWasTableRow = NO;
}

- (void) render: (EVDRenderContext &) aCtx : (DoRenderedPara) aDoRendered : (DoPageEnded) aPageEnded
{
    [self renderParas: aCtx : ^(id<IevdPara> vPara) {
        [vPara render: aCtx : aDoRendered : ^() {
            if ([self canBreak])
                aPageEnded();
        }];
    }];
}

@implementation EVDHorzParaList

- (void) render: (EVDRenderContext &) aCtx : (DoRenderedPara) aDoRendered : (DoPageEnded) aPageEnded
{
    CGRect vPrevRect = aCtx.rRect;
    __block CGFloat vRowWidth = 0;
    {
        __block CGRect vRect = vPrevRect;
        __block EVDParaIndexEx vCursor;
        __block EVDRenderContext vCtx (vCursor, vRect, aCtx.rParaAdded);
        [self renderParas: vCtx : ^(id<IevdPara> vPara) {
            vRowWidth += [vPara width];
        }];
    }
    
    __block CGFloat vHeight = std::numeric_limits<int>::max();
    __block BOOL vPageEnded = NO;
    __block EVDParaIndexEx::InnerCursors vInner;
    __block EVDParaIndexEx::InnerCursors::const_iterator vIt = aCtx.rCursor.rInnerCursors.begin();
    {
        __block CGRect vRect = vPrevRect;
        __block EVDParaIndexEx vCursor;
        __block EVDRenderContext vCtx (vCursor, vRect, aCtx.rParaAdded);
        [self renderParas: vCtx : ^(id<IevdPara> vPara) {
            CGFloat vCellWidth = (vPrevRect.size.width * [vPara width]) / vRowWidth;
            CGRect vParaRect = vRect;
            vParaRect.size.width = vCellWidth - 8;
            if ([vPara hasFrame]) {
                vParaRect.origin.x += 3;
                vParaRect.size.width -= 3;
            }
            vParaRect.origin.y = aCtx.rRect.origin.y;
            vParaRect.size.height = aCtx.rRect.size.height;
            EVDParaIndexEx vParaCursor;
            
            if (vIt != aCtx.rCursor.rInnerCursors.end()) {
                vParaCursor = *vIt;
                vIt++;
            }
            
            EVDRenderContext vParaCtx (vParaCursor, vParaRect, vCtx.rParaAdded);
            [vPara render: vParaCtx : aDoRendered : ^() {
                vPageEnded = YES;
            }];
            vInner.push_back(vParaCursor);
            vRect.origin.x += vCellWidth;
            vHeight = MIN(vHeight, vParaRect.size.height);
        }];
    }
    
    EVDTableRowFrame *vRowFrame = [[EVDTableRowFrame alloc] initWithFrame: NULL andPara: self];
    @try {
        CGFloat vDH = vPrevRect.size.height - vHeight;
        __block CGRect vRect = vPrevRect;
        vRect.origin.x -= 4;
        __block EVDParaIndexEx vCursor;
        __block EVDRenderContext vCtx (vCursor, vPrevRect, aCtx.rParaAdded);
        __block BOOL vIsFirst = YES;
        [self renderParas: vCtx : ^(id<IevdPara> vPara) {
            CGFloat vCellWidth = (vPrevRect.size.width * [vPara width]) / vRowWidth;
            if ([vPara hasFrame]) {
                vRect.origin.y = vHeight + 23;
                vRect.size.height = vDH;
                vRect.size.width = vCellWidth;
                EVDRenderedFrame * vFrame = [[EVDRenderedFrame alloc] initWithRect: vRect andPara: vPara];
                @try{
                    if (vIsFirst) {
                        vIsFirst = NO;
                        vFrame->myIsFirst = YES;
                    }
                    if (!aCtx.rWasTableRow) {
                        vFrame->myIsFirstRow = YES;
                    }
                    [vRowFrame addRendered: vFrame];
                }
                @finally {
                    DESTROY(vFrame);
                }
            }
            vRect.origin.x += vCellWidth;
        }];
        if ([vRowFrame hasRendered])
            aDoRendered(vRowFrame);
    }
    @finally {
        DESTROY(vRowFrame);
    }
    
    if (vPageEnded) {
        aCtx.rCursor.rInnerCursors = vInner;
        aCtx.rCursor.rPara--;
        aPageEnded();
    }
    else {
        aCtx.rCursor.rInnerCursors.clear();
        aCtx.rRect.size.height = vHeight;
    }
    aCtx.rWasTableRow = YES;
}

- (BOOL) canBreak
{
    return NO;
}

@end

- (void) draw: (CGContextRef) aContext forDoc: (id<IevdDocument>) aDoc
{
    
    EVDSelection * vSelection = [aDoc selection];
    if (vSelection) {
        NodeKey vNodeKey = [myPara NodeKey];
        EVDParaCursorLite vCursor(vNodeKey, [myPara indexInParent], 0, [[aDoc blockByKey: vNodeKey] ZOrder]);
        if (vSelection->Has(vCursor))
            [self fillPara: aContext withBackColor: [UIColor colorWithRed: 0.0627451 green: 0.419608 blue: 0.745098 alpha: 0.3] : NO];
    }
    
    [self fillPara: aContext withBackColor: [myPara backColor] : YES];
    
    const EVDImage &vImage = [myPara headerImage: aDoc];
    if (vImage.Image())
    {
        CTFrameRef vFrame = myFrame;
        CGPathRef vFramePath = CTFrameGetPath(vFrame);
        CGRect vFrameRect = CGPathGetBoundingBox(vFramePath);
        NSArray *vLines = (NSArray *)CTFrameGetLines(vFrame);
        
        int vLineCount = [vLines count];
        
        if (vLineCount > 0) {
            CGPoint vLineOrigin;
            CTFrameGetLineOrigins(vFrame, CFRangeMake(0, 1), &vLineOrigin);
            //                CTFrameGetLineOrigins(aFrame, CFRangeMake(vLineCount - 1, 1), &vLineOrigin);
            
            vLineOrigin.y -= 6;
            //                vLineOrigin.y -= 5;
            //                    vFrameRect.size.height -= vLineOrigin.y;
            vFrameRect.size.width = vImage.Width();
            vFrameRect.size.height = vImage.Height();
            vFrameRect.origin.y += vLineOrigin.y;
            vFrameRect.origin.x += 10;
            CGContextDrawImage(aContext, vFrameRect, vImage.Image().CGImage);
        }
    }
    
    [aDoc fillStringToSearch: ^(const NSRange & aRange){
        [self fillSelected: aContext range: aRange];
    } forPara: myPara];
    
    CTFrameDraw(myFrame, aContext);
    
    CFAttributedStringRef vText = myPara.Text;
    if (vText) {
        [self checkSelected: NSMakeRange(0, CFAttributedStringGetLength(vText)) do: ^(const GlyphData & aData)
         {
             CFTypeRef vAttr = CFAttributedStringGetAttribute(vText, aData.rGlyphIndex, kCTRunDelegateAttributeName, NULL);
             if (vAttr) {
                 CTRunDelegateRef delegate = (CTRunDelegateRef)vAttr;
                 NSDictionary *vDict = (NSDictionary*)CTRunDelegateGetRefCon(delegate);
                 if (vDict) {
                     CGRect vContextRect = CGContextGetClipBoundingBox(aContext);
                     UIImage *vGlyphImage = [vDict objectForKey:@"image"];
                     CGFloat vWidth = MIN([(NSString*)[vDict objectForKey:@"width"] floatValue], vContextRect.size.width - 40);
                     CGFloat vHeight = [(NSString*)[vDict objectForKey:@"height"] floatValue];
                     CGFloat vGap = [(NSString*)[vDict objectForKey:@"gap"] floatValue];
                     vWidth -= vGap;
                     if (vGlyphImage) {
                         CGContextDrawImage(aContext, CGRectMake(aData.rGlyphOrigin.x/* - vBox[0].origin.x*/, aData.rGlyphOrigin.y - 5/*- vBox[0].origin.y*/, vWidth, vHeight), vGlyphImage.CGImage);
                     }
                 }
             }
         }]; // [self checkSelected
    } // vText
} // vNeedDrawPara

- (void) draw: (CGContextRef) aContext forDoc: (id<IevdDocument>) aDoc
{
    int vCount = [myFrames count];
    for (int i = 0; i < vCount; i++) {
        [(EVDRenderedPara *)[myFrames objectAtIndex: i] draw: aContext forDoc: aDoc];
    }
}

- (void) draw: (CGContextRef) aContext forDoc: (id<IevdDocument>) aDoc
{
    /* Stroke a sequence of line segments one after another in `context'. The
     line segments are specified by `points', an array of `count' CGPoints.
     This function is equivalent to
     
     CGContextBeginPath(context);
     for (k = 0; k < count; k += 2) {
     CGContextMoveToPoint(context, s[k].x, s[k].y);
     CGContextAddLineToPoint(context, s[k+1].x, s[k+1].y);
     }
     CGContextStrokePath(context); */
    
    id<IevdPara> vPara = [self para];
    
    if (vPara) {
        CGContextSetLineWidth(aContext, 1.0);
        CGContextBeginPath(aContext);
        
        if ([vPara prev] == nil) {
            // левая рамка
            CGContextMoveToPoint(aContext, myRect.origin.x, myRect.origin.y);
            CGContextAddLineToPoint(aContext, myRect.origin.x, myRect.origin.y + myRect.size.height);
        }
        
        // правая рамка
        CGContextMoveToPoint(aContext, myRect.origin.x + myRect.size.width, myRect.origin.y);
        CGContextAddLineToPoint(aContext, myRect.origin.x + myRect.size.width, myRect.origin.y + myRect.size.height);
        
        if (([vPara mergeStatus] != EVD::ms_Head) || ([[myRow para] next] == nil)) {
            // нижняя рамка
            CGContextMoveToPoint(aContext, myRect.origin.x, myRect.origin.y);
            CGContextAddLineToPoint(aContext, myRect.origin.x + myRect.size.width, myRect.origin.y);
        }
        
        if (myIsFirstRow) {
            // верхняя рамка
            CGContextMoveToPoint(aContext, myRect.origin.x, myRect.origin.y + myRect.size.height);
            CGContextAddLineToPoint(aContext, myRect.origin.x + myRect.size.width, myRect.origin.y + myRect.size.height);
        }
        CGContextStrokePath(aContext);
    }
    
//    CGContextStrokeRect(aContext, myRect);
}

2 комментария:

  1. Почти дельфийский код. Многое знакомо...

    ОтветитьУдалить
    Ответы
    1. "Почти дельфийский код."
      -- я старался "сохранять стиль".

      Удалить