【问题标题】:Draw image after content in PDF file在 PDF 文件中的内容之后绘制图像
【发布时间】:2013-01-04 09:26:08
【问题描述】:

我最近一直在寻找好的 PDF 教程、文档等。

我最终使用了this 代码,但问题很少。

场景

我有一个包含标签、textView 和 imageView 的视图。 现在,我们将调用标签name、textView description 和imageView image

名称用作标题。

描述非常可变,可以从2行到一些页面。

图片应该放在描述文本的末尾。

我正在使用此代码:

- (void)generatePDF{
    NSString *fileName = [NSString stringWithFormat:@"%@.pdf",nameString];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *pdfFileName = [documentsDirectory stringByAppendingPathComponent:fileName];
    
    CFAttributedStringRef currentText = CFAttributedStringCreate(NULL,
                                                                 (CFStringRef)descriptionString, NULL);
    if (currentText) {
        CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(currentText);
        if (framesetter) {
                        
            // Create the PDF context using the default page: currently constants at the size
            // of 612 x 792.
            UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);
            
            CFRange currentRange = CFRangeMake(0, 0);
            NSInteger currentPage = 0;
            BOOL done = NO;
            
            do {
                // Mark the beginning of a new page.
                UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, kDefaultPageWidth,
                                                          kDefaultPageHeight), nil);
                [self drawHeader]
                // Draw a page number at the bottom of each page
                currentPage++;
                [self drawPageNumber:currentPage];
                
                // Render the current page and update the current range to
                // point to the beginning of the next page.
                currentRange = [self renderPage:currentPage withTextRange:
                                currentRange andFramesetter:framesetter];
                
                // If we're at the end of the text, exit the loop.
                if (currentRange.location == CFAttributedStringGetLength
                    ((CFAttributedStringRef)currentText))
                    done = YES;
            } while (!done);
            
            // Close the PDF context and write the contents out.
            UIGraphicsEndPDFContext();
            
            // Release the framewetter.
            CFRelease(framesetter);
            
        } else {
            NSLog(@"Could not create the framesetter needed to lay out the atrributed string.");
        }
        // Release the attributed string.
        CFRelease(currentText);
    } else {
        NSLog(@"Could not create the attributed string for the framesetter");
    }

}

- (CFRange)renderPage:(NSInteger)pageNum withTextRange:(CFRange)currentRange
       andFramesetter:(CTFramesetterRef)framesetter
{
    // Get the graphics context.
    CGContextRef    currentContext = UIGraphicsGetCurrentContext();
    
    // Put the text matrix into a known state. This ensures
    // that no old scaling factors are left in place.
    CGContextSetTextMatrix(currentContext, CGAffineTransformIdentity);
    
    // Create a path object to enclose the text. Use 72 point
    // margins all around the text.
    CGRect    frameRect = CGRectMake(22,72, 468, 648);
    CGMutablePathRef framePath = CGPathCreateMutable();
    CGPathAddRect(framePath, NULL, frameRect);
    
    // Get the frame that will do the rendering.
    // The currentRange variable specifies only the starting point. The framesetter
    // lays out as much text as will fit into the frame.
    CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, currentRange, framePath, NULL);
    CGPathRelease(framePath);
    
    // Core Text draws from the bottom-left corner up, so flip
    // the current transform prior to drawing.
    CGContextTranslateCTM(currentContext, 0, kDefaultPageHeight);
    CGContextScaleCTM(currentContext, 1.0, -1.0);
    
    // Draw the frame.
    CTFrameDraw(frameRef, currentContext);
    
    // Update the current range based on what was drawn.
    currentRange = CTFrameGetVisibleStringRange(frameRef);
    currentRange.location += currentRange.length;
    currentRange.length = 0;
    CFRelease(frameRef);
    
    return currentRange;
}


- (void)drawPageNumber:(NSInteger)pageNum
{
    NSString* pageString = [NSString stringWithFormat:NSLocalizedString(@"Page", nil), pageNum];
    UIFont* theFont = [UIFont systemFontOfSize:12];
    CGSize maxSize = CGSizeMake(kDefaultPageWidth, 72);
    
    CGSize pageStringSize = [pageString sizeWithFont:theFont
                                   constrainedToSize:maxSize
                                       lineBreakMode:UILineBreakModeClip];
    CGRect stringRect = CGRectMake(((kDefaultPageWidth - pageStringSize.width) / 2.0),
                                   720.0 + ((72.0 - pageStringSize.height) / 2.0) ,
                                   pageStringSize.width,
                                   pageStringSize.height);
    
    [pageString drawInRect:stringRect withFont:theFont];
}

我想知道如何在页面末尾、描述结束之后绘制图像。

我是这样画标题的:

-(void)drawHeader{
    NSString *headerString = nameString;
    UIFont* theFont = [UIFont boldSystemFontOfSize:15];
    CGSize maxSize = CGSizeMake(kDefaultPageWidth, 72);
    
    CGSize pageStringSize = [headerString sizeWithFont:theFont constrainedToSize:maxSize lineBreakMode:UILineBreakModeClip];
    CGRect stringRect = CGRectMake(22,22,pageStringSize.width,pageStringSize.height);
    
    [headerString drawInRect:stringRect withFont:theFont];
}

它显示在每一页的开头。

现在我不知道如何在内容(描述)之后绘制图像!

【问题讨论】:

标签: ios objective-c pdf


【解决方案1】:

首先您在pdf_tutorialViewController.h 文件中创建自定义方法

- (void)drawImage:(UIImage *)img  atRect:(CGRect)rect inContext:(CGContextRef)ctx;

///////////// pdf_tutorialViewController.m 文件 //////////

并在你的方法末尾添加UIImage并调用drawImage:atRect:inContext:方法。

- (CFRange)renderPage:(NSInteger)pageNum withTextRange:(CFRange)currentRange
       andFramesetter:(CTFramesetterRef)framesetter
{
  ////////////////////////////////////////////////////////////////////////////////////
       UIImage *logoimg = [UIImage imageNamed:@"1.png"];
       [self drawImage:logoimg atRect:CGRectMake("Your Size") inContext:currentContext];
       return currentRange;
  ////////////////////////////////////////////////////////////////////////////////////
} 

并添加代码

- (void)drawImage:(UIImage *)img  atRect:(CGRect)rect inContext:(CGContextRef)ctx
{
    UIImage *myImage = (UIImage *)img;
    [myImage drawInRect:rect];
    //[myImage release];
}

【讨论】:

  • 首先感谢您的回答!然后,在 CGRectMake("Your Size") 处,问题是我不知道该放入哪个尺寸,因为描述非常多变!有没有办法做到这一点?因为现在图像出现在每一页,我希望它只出现在页面的末尾,描述内容结束的地方!
  • CGRectMake(200, 55, 200, 300) 使用它
  • 有了那个坐标,它似乎与文本重叠,加上是颠倒的! :\
  • 然后输入CGRectMake ("As You Need"),因为不知道...你的文本在哪里结束。
【解决方案2】:

在上下文中绘制 pdf 就像在画布中绘制一样。

绘制本质上是动态的文本和图像的最佳方法是创建和使用方法来绘制,它返回用于在页面中绘制的高度,并简单地保留一个变量以通过递增来计算下一个绘图起点y位置与返回值。还要放一个单独的偏移变量来检查页框。

要将图像绘制到 pdf 上下文中,您可以使用以下示例。您还可以通过 Core Graphics 渲染 CGImage,但您必须考虑画布的旋转(CGContext),因为它们会出现在颠倒方式(翻转),因为 Core Graphics 中的坐标原点在右下角。

UIImage *gymLogo=[UIImage imageNamed:@"logo.png"];
CGPoint drawingLogoOrigin = CGPointMake(5,5);
[gymLogo drawAtPoint:drawingLogoOrigin];

【讨论】:

  • 感谢您的回答!这是我第一次使用 CoreText,我仍然很困惑!我会尝试这种方式,但我总是必须手动绘制 CGPoint 吗?我的意思是,我知道你提到了绘图+变量的事情,但是我不知道内容之后该怎么做!我可以在 -(CFRange)renderPage:(NSInteger)pageNum withTextRange:(CFRange)currentRange andFramesetter:(CTFramesetterRef)framesetter 方法中做到这一点吗?或者,如果我想在标题之后绘制图像,然后是内容,可以吗?
  • 一个不错的pdf教程:raywenderlich.com/6581/…
  • 是的,您可以将图像放置在您想要的任何位置,但关键在于您如何为其设置框架
  • 也检查这个问题:stackoverflow.com/questions/9511956/…
  • 它们没有旋转,它们被垂直翻转(镜像)
【解决方案3】:
NSString *fileName = @"myInfo.pdf";
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfFileName = [documentsDirectory stringByAppendingPathComponent:fileName];

if ([passwordString length]) {
    NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:passwordString, kCGPDFContextOwnerPassword, passwordString, kCGPDFContextUserPassword, kCFBooleanFalse, kCGPDFContextAllowsCopying, kCFBooleanFalse, kCGPDFContextAllowsPrinting,  nil]; // Providing Password Protection for PDF File
    UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, dictionary);
    [dictionary release];
} else {
    UIGraphicsBeginPDFContextToFile(pdfFileName, CGRectZero, nil);
}

UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, 612.0, heightOfView), nil); //612,792 Width  & Height of the pdf page which we want to generate.
NSString *pngPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%d.png", 123]];
UIImage *pngImage = [UIImage imageWithContentsOfFile:pngPath];

[pngImage drawInRect:CGRectMake((612 - self.frame.size.width) / 2, 0, self.frame.size.width, heightOfView)]; // Size of the image we want to draw in pdf file.
UIGraphicsEndPDFContext();

【讨论】:

    【解决方案4】:

    这是 UIView 的一个子类,可以满足您的需求...

    您可以将其放在UIScrollView 中。调用-sizeThatFits: 在您的superview 的-layoutSubviews 期间获得正确的帧大小。 (也适用于封闭滚动视图的 contentSize。)

    请注意,我在 UILabel 上包含了一个类别,该类别将合理的大小返回到 -sizeThatFits:,您可能需要提供自己的实现。

    View.h

    #import <UIKit/UIKit.h>
    
    @interface View : UIView
    @property ( nonatomic, strong, readonly ) UILabel * descriptionLabel ;
    @property ( nonatomic, strong, readonly ) UILabel * nameLabel ;
    @property ( nonatomic, strong, readonly ) UIImageView * pdfImageView ;
    
    @property ( nonatomic ) CGPDFDocumentRef pdf ;
    
    @end
    

    View.m

    #import "View.h"
    
    @implementation UILabel (SizeThatFits)
    -(CGSize)sizeThatFits:(CGSize)fitSize
    {
        return [ self.text sizeWithFont:self.font constrainedToSize:fitSize ] ;
    }
    @end
    
    @interface View ()
    @property ( nonatomic, strong, readonly ) UIImage * pdfImage ;
    @end
    
    @implementation View
    @synthesize descriptionLabel = _descriptionLabel ;
    @synthesize nameLabel = _nameLabel ;
    @synthesize pdfImageView = _pdfImageView ;
    @synthesize pdfImage = _pdfImage ;
    
    -(void)dealloc
    {
        CGPDFDocumentRelease( _pdf ) ;
    }
    
    -(CGSize)sizeThatFits:(CGSize)size
    {
        CGSize fitSize = (CGSize){ size.width, CGFLOAT_MAX } ;
    
        CGSize result = { size.width, 0 } ;
        result.height = [ self.headerLabel sizeThatFits:fitSize ].height
            + [ self.label sizeThatFits:fitSize ].height
            + self.pdfImage.size.height * fitSize.width / self.pdfImage.size.width ;
        return result ;
    }
    
    -(void)layoutSubviews
    {
        CGRect bounds = self.bounds ;
        CGRect slice ;
    
        CGRectDivide( bounds, &slice, &bounds, [ self.headerLabel sizeThatFits:bounds.size ].height, CGRectMinYEdge ) ;
        self.headerLabel.frame = slice ;
    
        CGRectDivide( bounds, &slice, &bounds, [ self.label sizeThatFits:bounds.size ].height, CGRectMinYEdge ) ;
        self.label.frame = slice ;
    
        self.pdfImageView.frame = bounds ;
    }
    
    -(void)setPdf:(CGPDFDocumentRef)pdf
    {
        CGPDFDocumentRelease( _pdf ) ;
        _pdf = CGPDFDocumentRetain( pdf ) ;
        _pdfImage = nil ;
    }
    
    -(UILabel *)descriptionLabel
    {
        if ( !_descriptionLabel )
        {
            UILabel * label = [[ UILabel alloc ] initWithFrame:CGRectZero ] ;
            label.numberOfLines = 0 ;
            //
            // ... configure label here
            //
            [ self addSubview:label ] ;
            _descriptionLabel = label ;
        }
        return _descriptionLabel ;
    }
    
    -(UILabel *)nameLabel
    {
        if ( !_nameLabel )
        {
            UILabel * label = [[ UILabel alloc ] initWithFrame:CGRectZero ] ;
            label.numberOfLines = 0 ;
            //
            // ... configure label here
            //
            [ self addSubview:label ] ;
            _nameLabel = label ;
        }
        return _nameLabel ;
    }
    
    -(UIView *)pdfImageView
    {
        if ( !_pdfImageView )
        {
            UIImageView * imageView = [[ UIImageView alloc ] initWithFrame:CGRectZero ] ;
            [ self addSubview:imageView ] ;
            _pdfImageView = imageView ;
        }
    
        return _pdfImageView ;
    }
    
    -(UIImage *)pdfImage
    {
        if ( !_pdfImage )
        {
            CGPDFPageRef page = CGPDFDocumentGetPage( self.pdf, 1 ) ; // 1 indexed
            CGRect mediaBox = CGPDFPageGetBoxRect( page, kCGPDFMediaBox ) ;
            UIGraphicsBeginImageContextWithOptions( mediaBox.size, NO, self.window.screen.scale ) ;
    
            CGContextRef c = UIGraphicsGetCurrentContext() ;
            CGContextScaleCTM( c, 1.0f, -1.0f ) ;
            CGContextTranslateCTM( c, 0.0, -mediaBox.size.height ) ;
    
            CGContextDrawPDFPage( c, page ) ;
    
            _pdfImage = UIGraphicsGetImageFromCurrentImageContext() ;
    
            self.pdfImageView.image = _pdfImage ;
    
            UIGraphicsEndImageContext() ;
        }
    
        return _pdfImage ;
    }
    
    @end
    

    如果需要页码页脚,可以在底部添加另一个 UILabel,遵循相同的模式。

    【讨论】:

    • 感谢您的回答!我昨天在尝试,我看到如果我在语句 if (currentRange.location == CFAttributedStringGetLength ((CFAttributedStringRef)currentText)) 之后放置一个 [self drawImage] 可以很容易地在最后绘制图像我会试试你的版本和我会告诉你的!
    • 无论你觉得哪个更容易——我喜欢这个,因为它只有 3 个子视图……性能也应该不错。如果您想要多个页面,只需创建多个视图。
    • 谢谢!我会告诉你! :) 感谢您的宝贵时间
    猜你喜欢
    • 2018-09-16
    • 1970-01-01
    • 2019-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-08
    • 1970-01-01
    • 2015-06-11
    相关资源
    最近更新 更多