在 CALayer 中用不同的颜色绘制 PDF。 (目标-C)

问题描述 投票:0回答:1

我已将黑色透明的 .PDF 图标加载到 CGPDFPageRef 中,并需要将其绘制到具有指定颜色的 CALayer 中。我可以正确缩放和旋转它,但不知道如何更改它渲染的颜色。

这是 macOS 桌面应用程序的一部分,用于绘制飞行中的飞机地图。我一直在使用 NSImage 绘制 .PNG 飞机图标,但由于我的源图标是 128x128,当 NSImage 缩小到 40x40 时,它们通常会显得有点模糊。我已经将图标转换为 128x128 矢量 PDF 文件,下面的代码在 CALayer 上正确缩放和定位它们,但我似乎无法更改它们绘制的颜色。

当我使用 NSImages 加载 .png 时,我有一些代码将 .png 读取到 NSImage,然后使用更改后的颜色创建一个新的 NSImage,我怀疑我需要对 PDF 执行相同的操作,但不知道如何这样做。

我将 .PDF 图标渲染到地图的代码是:

// Render a PDF vector icon
- (void)drawAircraftInContext:(CGContextRef)context acft:(Aircraft *)acft
{
   CGContextSaveGState (context);

   CGRect pdfRect = CGPDFPageGetBoxRect (acft.pdf, kCGPDFCropBox);

   CGContextTranslateCTM (context, acft.x, acft.y);
   CGContextRotateCTM (context, degreesToRadians (acft.heading));
   CGContextTranslateCTM (context, (_iconSize / 2), (_iconSize / 2));
   CGContextScaleCTM (context, .0 - (_iconSize / pdfRect.size.width), .0 - (_iconSize / pdfRect.size.height));

// None of this block actually works - the colour remains black with a clear background.
   CGContextSetAlpha (context, _radarView.militaryAlpha);
   CGContextSetFillColorWithColor (context, _militaryColour);
   CGContextSetStrokeColorWithColor (context, _militaryColour);
   CGContextSetBlendMode (context, kCGBlendModeMultiply);
// How do we change the colour of the image?

   CGContextDrawPDFPage (context, acft.pdf);

   CGRect rect = CGRectMake (0.0 - (_iconSize / 2.0), 0.0 - (_iconSize / 2.0), _iconSize, _iconSize);

   // Draw a highlight
   if (acft == _radarView.selected && !_radarView.showOnlySelectedAircraft)
      [self drawAircraftHighlightInContext:context rect:rect acft:acft];

   CGContextRestoreGState (context);
}

加载PDF的代码是:

  //Experimental to see if PDF images can be treated as vectors rather than images.
  if (acft.pdfIcon)
     {
     NSURL *nsurl = [NSURL fileURLWithPath:filename isDirectory:NO];
     CFURLRef url = (CFURLRef)CFBridgingRetain(nsurl);
     CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL (url);
     CGPDFPageRef page = CGPDFDocumentGetPage (pdf, 1);

     /* Might need to create another CGPDFPageRef from the original and draw
        into it with the correct colour. */

     acft.pdf = page;
     }

然后创建一个 NSImage,如果我可以改变颜色,我就不需要这样做:

  image = [[NSImage alloc] initWithContentsOfFile:filename];

  /*
   * Our .png images are just black but we'll need to use the specified colour
   * to indicate military, civilian or simulator.
   */
  [image lockFocus];
  [[[NSColor colorWithCGColor:colour] colorWithAlphaComponent:alpha] set];
  NSRect imageRect = { NSZeroPoint, [image size] };
  NSRectFillUsingOperation (imageRect, NSCompositeSourceIn);
  [image unlockFocus];
  }
   if (image)
      {
      CGRect rect = CGRectMake (0.0, 0.0, _iconSize, _iconSize);

      return ([image CGImageForProposedRect:&rect
                                    context:nil
                                      hints:@{}]);
      }
  return ([image CGImageForProposedRect:&rect
                                context:nil
                                  hints:@{}]);
  }
objective-c calayer cgcontextdrawpdfpage
1个回答
0
投票

一些注意事项...

首先,正如我在评论中提到的,PDF 文件中的形状/路径可能很挑剔。

使用您链接到的原始 PDF - http://www.iridiumblue.com/pages/AW109.pdf :

AW109

并且,使用 AC130 的 PDF(不记得从哪里得到的):

AC130

可以编辑PDF文件中的路径来解决问题...但是,正如您所看到的,修复AC130可能是一项相当大的工作量。

如果是我,我会生成自己的

CGMutablePath
- 大多数 PDF / 矢量编辑器都可以导出各种格式的路径 - 例如 SVG,可以相当容易地转换为
CGPathMoveToPoint(...)
/
CGPathAddLineToPoint(...)
/
CGPathAddCurveToPoint(...)
/ 等等

如果您决定使用 PDF 文件并提取路径,这里有一些 示例 代码...


提取器.h

#import <Foundation/Foundation.h>
#import <PDFKit/PDFKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface Extractor : NSObject
+ (NSArray<id> *)extractVectorPathsFromPDF:(NSURL *)pdfURL;
@end

NS_ASSUME_NONNULL_END

提取器.m

#import "Extractor.h"

@implementation Extractor

void ExtractPathsFromContent(CGPDFScannerRef scanner, void *info);

void moveToCallback(CGPDFScannerRef scanner, void *info);
void lineToCallback(CGPDFScannerRef scanner, void *info);
void curveToCallback(CGPDFScannerRef scanner, void *info);
void closePathCallback(CGPDFScannerRef scanner, void *info);

CGMutablePathRef currentPath = NULL;

+ (NSArray<id> *)extractVectorPathsFromPDF:(NSURL *)pdfURL {
    PDFDocument *pdfDocument = [[PDFDocument alloc] initWithURL:pdfURL];
    if (!pdfDocument) {
        NSLog(@"Unable to load PDF document.");
        return nil;
    }
    
    NSMutableArray<id> *vectorPaths = [NSMutableArray array];
    
    // Iterate through all pages in the PDF document
    for (NSInteger pageIndex = 0; pageIndex < pdfDocument.pageCount; pageIndex++) {
        PDFPage *pdfPage = [pdfDocument pageAtIndex:pageIndex];
        if (!pdfPage) continue;
        
        CGPDFPageRef cgPage = pdfPage.pageRef;
        if (!cgPage) continue;
        
        // Set up the scanner to process PDF content streams
        CGPDFContentStreamRef contentStream = CGPDFContentStreamCreateWithPage(cgPage);
        CGPDFOperatorTableRef operatorTable = CGPDFOperatorTableCreate();
        
        // Register custom callbacks for common operators for path extraction
        CGPDFOperatorTableSetCallback(operatorTable, "m", &moveToCallback);  // move to
        CGPDFOperatorTableSetCallback(operatorTable, "l", &lineToCallback);  // line to
        CGPDFOperatorTableSetCallback(operatorTable, "c", &curveToCallback); // curve to
        CGPDFOperatorTableSetCallback(operatorTable, "h", &closePathCallback); // close path
        
        CGPDFScannerRef scanner = CGPDFScannerCreate(contentStream, operatorTable, (__bridge void *)(vectorPaths));
        
        // Initialize a new path for the page
        currentPath = CGPathCreateMutable();
        
        // Scan the PDF content
        CGPDFScannerScan(scanner);
        
        // After scanning, add the current path to the paths array
        if (currentPath) {
            [vectorPaths addObject:(__bridge_transfer id)currentPath];
            currentPath = NULL;
        }
        
        // Clean up
        CGPDFScannerRelease(scanner);
        CGPDFContentStreamRelease(contentStream);
        CGPDFOperatorTableRelease(operatorTable);
    }
    
    return [vectorPaths copy];
}

// Callback for "move to" operator
void moveToCallback(CGPDFScannerRef scanner, void *info) {
    CGPDFReal x, y;
    if (CGPDFScannerPopNumber(scanner, &y) && CGPDFScannerPopNumber(scanner, &x)) {
        CGPathMoveToPoint(currentPath, NULL, x, y);
    }
}

// Callback for "line to" operator
void lineToCallback(CGPDFScannerRef scanner, void *info) {
    CGPDFReal x, y;
    if (CGPDFScannerPopNumber(scanner, &y) && CGPDFScannerPopNumber(scanner, &x)) {
        CGPathAddLineToPoint(currentPath, NULL, x, y);
    }
}

// Callback for "curve to" operator
void curveToCallback(CGPDFScannerRef scanner, void *info) {
    CGPDFReal x1, y1, x2, y2, x3, y3;
    if (CGPDFScannerPopNumber(scanner, &y3) && CGPDFScannerPopNumber(scanner, &x3) &&
        CGPDFScannerPopNumber(scanner, &y2) && CGPDFScannerPopNumber(scanner, &x2) &&
        CGPDFScannerPopNumber(scanner, &y1) && CGPDFScannerPopNumber(scanner, &x1)) {
        CGPathAddCurveToPoint(currentPath, NULL, x1, y1, x2, y2, x3, y3);
    }
}

// Callback for "close path" operator
void closePathCallback(CGPDFScannerRef scanner, void *info) {
    CGPathCloseSubpath(currentPath);
}

@end

可以这样使用:

NSString *pdfName = @"AW109";
NSString *pdfPath = [[NSBundle mainBundle] pathForResource:pdfName ofType:@"pdf"];
NSURL *pdfUrl = [NSURL fileURLWithPath:pdfPath isDirectory:NO];

// get the paths from the PDF
//  the PDF has only one page, so we're expecting only one path
NSArray<id> *vectorPaths = [Extractor extractVectorPathsFromPDF:pdfUrl];
CGPathRef pth = (CGPathRef)CFBridgingRetain([vectorPaths firstObject]);

// we now have a CGPathRef which can be drawn using CGContextDrawPath
//  or set as the .path of a CAShapeLayer

另一种方法,可能是更好的选择 - 使用

NSImageView
将 PDF 文件作为
NSImage
,然后缩放/旋转/定位该图像视图作为子视图:

NSImageView *imgView = [NSImageView new];
imgView = [NSImageView new];
imgView.wantsLayer = YES;
imgView.imageScaling = NSImageScaleProportionallyUpOrDown;

// desired color
imgView.contentTintColor = NSColor.redColor;

NSString *pdfName = @"AW109";
NSString *pdfPath = [[NSBundle mainBundle] pathForResource:pdfName ofType:@"pdf"];
NSURL *pdfUrl = [NSURL fileURLWithPath:pdfPath isDirectory:NO];

NSImage *img = [[NSImage alloc] initWithContentsOfURL:pdfUrl];
img.template = YES;
imgView.image = img;

经过一些快速测试,Cocoa 似乎在处理 PDF 文件方面做得很好,并在渲染时缩放其路径

例如 - 原始 AW109.pdf,缩放为 3 种尺寸(图像视图也分别旋转 45°):

Image Views


以上所有图像都放置在“棋盘图案”之上,以便我们可以看到透明度。

© www.soinside.com 2019 - 2024. All rights reserved.