Parse whole pdf-page to NSString on an iPhone - ios

I've been trying to parse a pdf-page of text to NSString for a while now and the only thing I can find are methods to search for specific stringvalues.
What I'd like to do is parse a single page of PDF without using any external libraries such as PDFKitten, PDFKit etc.
I'd like to have the data in an NSArray, NSString or NSDictionary if possible.
Thanks :D!
A piece of what I've tried so far.
CGPDFDocumentRef MyGetPDFDocumentRef (const char *filename) {
CFStringRef path;
CFURLRef url;
CGPDFDocumentRef document;
path = CFStringCreateWithCString (NULL, filename,kCFStringEncodingUTF8);
url = CFURLCreateWithFileSystemPath (NULL, path, kCFURLPOSIXPathStyle, 0);
CFRelease (path);
document = CGPDFDocumentCreateWithURL (url);// 2
CFRelease(url);
int count = CGPDFDocumentGetNumberOfPages (document);// 3
if (count == 0) {
printf("`%s' needs at least one page!", filename);
return NULL;
}
return document;
}
// table methods to parse pdf
static void op_MP (CGPDFScannerRef s, void *info) {
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
printf("MP /%s\n", name);
}
static void op_DP (CGPDFScannerRef s, void *info) {
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
printf("DP /%s\n", name);
}
static void op_BMC (CGPDFScannerRef s, void *info) {
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
printf("BMC /%s\n", name);
}
static void op_BDC (CGPDFScannerRef s, void *info) {
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
printf("BDC /%s\n", name);
}
static void op_EMC (CGPDFScannerRef s, void *info) {
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
printf("EMC /%s\n", name);
}
void MyDisplayPDFPage (CGContextRef myContext,size_t pageNumber,const char *filename) {
CGPDFDocumentRef document;
CGPDFPageRef page;
document = MyGetPDFDocumentRef (filename);// 1
totalPages=CGPDFDocumentGetNumberOfPages(document);
page = CGPDFDocumentGetPage (document, 1);// 2
CGPDFDictionaryRef d;
d = CGPDFPageGetDictionary(page);
CGPDFScannerRef myScanner;
CGPDFOperatorTableRef myTable;
myTable = CGPDFOperatorTableCreate();
CGPDFOperatorTableSetCallback (myTable, "MP", &op_MP);
CGPDFOperatorTableSetCallback (myTable, "DP", &op_DP);
CGPDFOperatorTableSetCallback (myTable, "BMC", &op_BMC);
CGPDFOperatorTableSetCallback (myTable, "BDC", &op_BDC);
CGPDFOperatorTableSetCallback (myTable, "EMC", &op_EMC);
CGPDFContentStreamRef myContentStream = CGPDFContentStreamCreateWithPage (page);// 3
myScanner = CGPDFScannerCreate (myContentStream, myTable, NULL);// 4
CGPDFScannerScan (myScanner);// 5
CGPDFStringRef str;
d = CGPDFPageGetDictionary(page);
if (CGPDFDictionaryGetString(d, "Lorem", &str)){
CFStringRef s;
s = CGPDFStringCopyTextString(str);
if (s != NULL) {
NSLog(#"%# testing it", s);
}
CFRelease(s);
}
}
- (void)viewDidLoad {
[super viewDidLoad];
MyDisplayPDFPage(UIGraphicsGetCurrentContext(), 1, [[[NSBundle mainBundle] pathForResource:#"TestPage" ofType:#"pdf"] UTF8String]);
}

Quartz provides functions that let you inspect the PDF document structure and the content stream. Inspecting the document structure lets you read the entries in the document catalog and the contents associated with each entry. By recursively traversing the catalog, you can inspect the entire document.
A PDF content stream is just what its name suggests—a sequential stream of data such as 'BT 12 /F71 Tf (draw this text) Tj . . . ' where PDF operators and their descriptors are mixed with the actual PDF content. Inspecting the content stream requires that you access it sequentially.
This developer.apple documentation shows how to examine the structure of a PDF document and parse the contents of a PDF document.

Related

How to hook Apple's `os_log_with_type` method in Objective-C

I want to hook "React Native" RCTLog logs method _RCTLogJavaScriptInternal , The source codes is:
void _RCTLogJavaScriptInternal(RCTLogLevel level, NSString *message)
{
RCTLogFunction logFunction = RCTGetLocalLogFunction();
BOOL log = RCT_DEBUG || (logFunction != nil);
if (log && level >= RCTGetLogThreshold()) {
if (logFunction) {
logFunction(level, RCTLogSourceJavaScript, nil, nil, message);
}
}
}
RCTLogFunction RCTDefaultLogFunction =
^(RCTLogLevel level,
RCTLogSource source,
__unused NSString *fileName,
__unused NSNumber *lineNumber,
NSString *message) {
os_log_with_type(RCTLogForLogSource(source), RCTLogTypeForLogLevel(level), "%{public}s", message.UTF8String);
};
So if I just hook Apple's os_log_with_type, I will get the RCTLog logs.
This is my codes, but not working. Please help me. Thanks!!!!
#import <os/log.h>
#import "fishhook.h"
static void (*original_oslog)((os_log_t log, os_log_type_t type, const char *format, ...));
void hook_oslog(os_log_t log, os_log_type_t type, const char *format, ...) {
NSLog(#"hook success!");
}
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
struct rebinding oslog_rebinding = { "os_log_with_type", hook_oslog, (void *)&original_oslog };
rebind_symbols((struct rebinding[1]){oslog_rebinding}, 1);
});
}
Final, I found solution. Not need to hook, Related API have been provided by React Native already.
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
NSLog(#"%#", message);
});

How do I convert flac to wav on iOS?

I have a file which is encoded with FLAC and I want to convert it to WAV.
I have added this FFMpeg lib to my project and imported it.
I see some code from this answer, but I am unclear on how to use it:
#import "avformat.h"
// Some code goes here
/*
* avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options)
*/
int openInputValue = avformat_open_input(&pFormatCtx, utf8FilePath, inputFormat, nil);
NSLog(#"%s - %d # openInputValue = %d", __PRETTY_FUNCTION__, __LINE__, openInputValue);
I currently have this code in a function which takes NSData holding the FLAC file. If avformat_open_input is the correct call, how do I set the variable? If it is not the correct call, what is?
This question seems like a duplicate but it doesn't really have a good answer.
Also note that I don't want a player. This file contains MQA so I need to run it through my own custom decoder.
I was able to implement this using this code for decoding and this code to actually write the WAV header/body.
As an added bonus, this was very helpful in decoding NSData instead of a file.
Here is my finished decoder, though I wouldn't expect it to work in any case except mine.
//
// FlacToWavConverter.m
// SuperpoweredMQAExample
//
// Created by Tony Lawrence on 5/18/17.
// Copyright © 2017 imect. All rights reserved.
//
#import "FlacToWavConverter.h"
#import "avformat.h"
#import "avcodec.h"
#import "avutil.h"
#import "swresample.h"
#import "file.h"
#implementation FlacToWavConverter
+(NSURL*)convertFlacToWav:(NSData*)data {
//const char* input_filename = [filePath UTF8String];
int buffer_size = 16384;
// This call is necessarily done once in your app to initialize
// libavformat to register all the muxers, demuxers and protocols.
av_register_all();
// A media container
AVFormatContext* container = avformat_alloc_context();
//Make a custom IO context so that we can read from memory instead of a file...
unsigned char* iobuffer = av_malloc(buffer_size);
struct buffer_data bd = { 0 };
bd.ptr = (uint8_t*)data.bytes;
bd.size = data.length;
AVIOContext* ioContext = avio_alloc_context(iobuffer, buffer_size, 0, &bd, &read_packet, NULL, NULL);
container->pb = ioContext;
if (avformat_open_input(&container, "arbitrary", NULL, NULL) < 0) {
NSLog(#"Could not open file");
}
if (avformat_find_stream_info(container, NULL) < 0) {
NSLog(#"Could not find file info");
}
int stream_id = -1;
// To find the first audio stream. This process may not be necessary
// if you can gurarantee that the container contains only the desired
// audio stream
int i;
for (i = 0; i < container->nb_streams; i++) {
if (container->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
stream_id = i;
break;
}
}
if (stream_id == -1) {
NSLog(#"Could not find an audio stream");
}
// Extract some metadata
AVDictionary* metadata = container->metadata;
// Find the apropriate codec and open it
AVCodecContext* codec_context = container->streams[stream_id]->codec;
AVCodec* codec = avcodec_find_decoder(codec_context->codec_id);
if (avcodec_open2(codec_context, codec, NULL) < 0) {
NSLog(#"Could not find open the needed codec");
}
NSMutableData *pcmFile = [NSMutableData new];
AVPacket packet;
int8_t buffer[buffer_size];
while (1) {
//buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
// Read one packet into `packet`
if (av_read_frame(container, &packet) < 0) {
break; // End of stream. Done decoding.
}
// Decodes from `packet` into the buffer
if (avcodec_decode_audio3(codec_context, (int16_t*)buffer, &buffer_size, &packet) < 1) {
break; // Error in decoding
}
// Send the buffer contents to the audio device
[pcmFile appendBytes:buffer length:buffer_size];
}
avformat_close_input(&container);
//fprintf(stdout, "Done playing. Exiting...");
NSURL *file = [FlacToWavConverter getAndCreatePlayableFileFromPcmData:pcmFile];
NSLog(#"Got a playable file maybe? %#", [file absoluteString]);
return file;
}
+(NSURL *) getAndCreatePlayableFileFromPcmData:(NSData *)data
{
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *wavFilePath = [docsDir stringByAppendingPathComponent:#"output.wav"];
//NSLog(#"PCM data : %#",data);
FILE *fout;
short NumChannels = 2;
short BitsPerSample = 16;
int SamplingRate = 44100;
int numOfSamples = [data length];
int ByteRate = NumChannels*BitsPerSample*SamplingRate/8;
short BlockAlign = NumChannels*BitsPerSample/8;
int DataSize = NumChannels*numOfSamples*BitsPerSample/8;
int chunkSize = 16;
int totalSize = 46 + DataSize;
short audioFormat = 1;
if((fout = fopen([wavFilePath cStringUsingEncoding:1], "w")) == NULL)
{
printf("Error opening out file ");
}
fwrite("RIFF", sizeof(char), 4,fout);
fwrite(&totalSize, sizeof(int), 1, fout);
fwrite("WAVE", sizeof(char), 4, fout);
fwrite("fmt ", sizeof(char), 4, fout);
fwrite(&chunkSize, sizeof(int),1,fout);
fwrite(&audioFormat, sizeof(short), 1, fout);
fwrite(&NumChannels, sizeof(short),1,fout);
fwrite(&SamplingRate, sizeof(int), 1, fout);
fwrite(&ByteRate, sizeof(int), 1, fout);
fwrite(&BlockAlign, sizeof(short), 1, fout);
fwrite(&BitsPerSample, sizeof(short), 1, fout);
fwrite("data", sizeof(char), 4, fout);
fwrite(&DataSize, sizeof(int), 1, fout);
fclose(fout);
NSMutableData *pamdata = [NSMutableData dataWithData:data];
NSFileHandle *handle;
handle = [NSFileHandle fileHandleForUpdatingAtPath:wavFilePath];
[handle seekToEndOfFile];
[handle writeData:pamdata];
[handle closeFile];
NSLog(#"Saved wav: %#", wavFilePath);
return [NSURL URLWithString:wavFilePath];
}
struct buffer_data {
uint8_t *ptr;
size_t size; ///< size left in the buffer
};
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
struct buffer_data *bd = (struct buffer_data *)opaque;
buf_size = FFMIN(buf_size, bd->size);
//printf("ptr:%p size:%zu\n", bd->ptr, bd->size);
/* copy internal buffer data to buf */
memcpy(buf, bd->ptr, buf_size);
bd->ptr += buf_size;
bd->size -= buf_size;
return buf_size;
}
#end

AES/CBC/PKCS5Padding in iOS objective c result differs from Android

I am using the AES/CBC/PKCS5Padding in Android application. Code is like-
private static String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private static String ALGORITHM = "AES";
private static String DIGEST = "MD5";
private static Cipher cipher;
private static SecretKey password;
private static IvParameterSpec IVParamSpec;
private final static String pvtkey="GDNBCGDRFSC$%#%=";
//16-byte private key
private static byte[] IV = pvtkey.getBytes();
public PassWordEncryptor() {
try {
//Encode digest
MessageDigest digest;
digest = MessageDigest.getInstance(DIGEST);
password = new SecretKeySpec(digest.digest(pvtkey.getBytes()), ALGORITHM);
//Initialize objects
cipher = Cipher.getInstance(TRANSFORMATION);
IVParamSpec = new IvParameterSpec(IV);
} catch (NoSuchAlgorithmException e) {
Log.i(Lams4gApp.TAG, "No such algorithm " + ALGORITHM);
} catch (NoSuchPaddingException e) {
System.out.println( "No such padding PKCS7"+ e);
}
}
/**
Encryptor.
#text String to be encrypted
#return Base64 encrypted text
*/
public String encrypt(byte[] text) {
byte[] encryptedData;
try {
cipher.init(Cipher.ENCRYPT_MODE, password, IVParamSpec);
encryptedData = cipher.doFinal(text);
} catch (InvalidKeyException e) {
System.out.println( "Invalid key (invalid encoding, wrong length, uninitialized, etc)."+ e);
return null;
} catch (InvalidAlgorithmParameterException e) {
System.out.println( "Invalid or inappropriate algorithm parameters for " + ALGORITHM+ e);
return null;
} catch (IllegalBlockSizeException e) {
System.out.println( "The length of data provided to a block cipher is incorrect"+ e);
return null;
} catch (BadPaddingException e) {
System.out.println( "The input data but the data is not padded properly."+ e);
return null;
}
return Base64.encodeToString(encryptedData,Base64.DEFAULT);
}
I need similar code in iOS Objective C. Encryption and Decryption results should be same in android and iOS.
Kindly provide the same algorithm for Objective C.
I am using iOS code as-
- (void)viewDidLoad {
[super viewDidLoad];
NSData *encodingData=[self encrypt:[#"slapkh"
dataUsingEncoding:NSUTF8StringEncoding]];
NSString *encodingResult = [NSString base64StringFromData:encodingData length:[encodingData length]];
}
- (NSData *) encrypt:(NSData *) plainText {
return [self transform:kCCEncrypt data:plainText];
}
- (NSData *) decrypt:(NSData *) cipherText {
return [self transform:kCCDecrypt data:cipherText];
}
- (NSData *) transform:(CCOperation) encryptOrDecrypt data:(NSData *) inputData {
Cipher* cipher = [[Cipher alloc]initWithKey:#"GDNBCGDRFSC$%#%="];
NSString* Key = cipher.cipherKey;
// kCCKeySizeAES128 = 16 bytes
// CC_MD5_DIGEST_LENGTH = 16 bytes
NSData* secretKey = [Cipher md5:Key];
CCCryptorRef cryptor = NULL;
CCCryptorStatus status = kCCSuccess;
uint8_t iv[kCCBlockSizeAES128];
memset((void *) iv, 0x0, (size_t) sizeof(iv));
status = CCCryptorCreate(encryptOrDecrypt,
kCCAlgorithmAES128,kCCOptionPKCS7Padding,
[secretKey bytes], kCCKeySizeAES128, iv, &cryptor);
if (status != kCCSuccess) {
return nil;
}
size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t)[inputData length],
true);
void * buf = malloc(bufsize * sizeof(uint8_t));
memset(buf, 0x0, bufsize);
size_t bufused = 0;
size_t bytesTotal = 0;
status = CCCryptorUpdate(cryptor, [inputData bytes], (size_t)[inputData length],
buf, bufsize, &bufused);
if (status != kCCSuccess) {
free(buf);
CCCryptorRelease(cryptor);
return nil;
}
bytesTotal += bufused;
status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused);
if (status != kCCSuccess) {
free(buf);
CCCryptorRelease(cryptor);
return nil;
}
bytesTotal += bufused;
CCCryptorRelease(cryptor);
return [NSData dataWithBytesNoCopy:buf length:bytesTotal];
}
But the results for Android and iOS Differs as-
Text to encrypt: slapkh
key: GDNBCGDRFSC$%#%=
Android result: jN2p1yAdBJLRmoHq+k9KtA==\n
iOS Resut: tbaSJFv5mGyZ9t/+kOw+gg==
After spending time dealing with this, I got success in ANDROID(java) and IOS (Objc) using AES with the codes below:
ANDROID CODE
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class SecurityUtils {
private static final String ALGORITHM = "AES";
private static final String MODE = "AES";
private static final String IV = "AEE0715D0778A4E4";
private static final String KEY= "9336365521W5F092BB5909E8E033BC69";
public static String encrypt(String value ) throws NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
SecretKeySpec secretKeySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(MODE);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(IV.getBytes()));
byte[] values = cipher.doFinal(value.getBytes());
return Base64.encodeBytes(values);
}
public static String decrypt(String value) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
byte[] values = Base64.decode(value);
SecretKeySpec secretKeySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(MODE);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(IV.getBytes()));
return new String(cipher.doFinal(values));
}
}
TESTING ANDROID
try {
String encrypted = SecurityUtils.encrypt("My Secret Text");
String decrypted = SecurityUtils.decrypt(encrypted);
Log.e("encrypted", encrypted);
Log.e("decrypted", decrypted);
}catch(Exception ex){
Log.e("AES", ex.getMessage());
}
IOS CODE
Header file
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCryptor.h>
NS_ASSUME_NONNULL_BEGIN
#interface SecurityUtils : NSObject
+ (NSString *)encrypt:(NSString *)plainText error:(NSError **)error;
+ (NSString *)decrypt:(NSString *)plainText error:(NSError **)error;
#end
NS_ASSUME_NONNULL_END
Implementation file
NSString *const IV = #"AEE0515D0B08A4E4";
NSString *const KEY = #"9336565521E5F082BB5929E8E033BC69";
#import "SecurityUtils.h"
#implementation SecurityUtils
+ (NSString *)encrypt:(NSString *)plainText error:(NSError **)error {
NSMutableData *result = [SecurityUtils doAES:[plainText dataUsingEncoding:NSUTF8StringEncoding] context: kCCEncrypt error:error];
return [result base64EncodedStringWithOptions:0];
}
+ (NSString *)decrypt:(NSString *)encryptedBase64String error:(NSError **)error {
NSData *dataToDecrypt = [[NSData alloc] initWithBase64EncodedString:encryptedBase64String options:0];
NSMutableData *result = [SecurityUtils doAES:dataToDecrypt context: kCCDecrypt error:error];
return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}
+ (NSMutableData *)doAES:(NSData *)dataIn context:(CCOperation)kCCEncrypt_or_kCCDecrypt error:(NSError **)error {
CCCryptorStatus ccStatus = kCCSuccess;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:dataIn.length + kCCBlockSizeBlowfish];
NSData *key =[KEY dataUsingEncoding:NSUTF8StringEncoding];
NSData *iv = [IV dataUsingEncoding:NSUTF8StringEncoding];
ccStatus = CCCrypt( kCCEncrypt_or_kCCDecrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
key.bytes,
key.length,
(iv)?nil:iv.bytes,
dataIn.bytes,
dataIn.length,
dataOut.mutableBytes,
dataOut.length,
&cryptBytes);
if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes;
}
else {
if (error) {
*error = [NSError errorWithDomain:#"kEncryptionError"
code:ccStatus
userInfo:nil];
}
dataOut = nil;
}
return dataOut;
}
#end
IOS TESTING
NSError *error;
NSString *encrypted = [SecurityUtils encrypt:#"My Secret Text" error:&error];
NSLog(#"encrypted: %#",encrypted);
NSLog(#"decrypted: %#",[SecurityUtils decrypt:encrypted error:&error]);
Finally, the outputs of the test:
ANDROID OUTPUT
2019-05-16 21:35:01.215 4920-4920/br.com.my.app E/encrypted: EJ41am5W1k6fA7ygFjTSEw==
2019-05-16 21:35:01.215 4920-4920/br.com.my.app E/decrypted: My Secret Text
IOS OUTPUT
2019-05-16 21:38:02.947043-0300 MyApp[63392:1590665] encrypted: EJ41am5W1k6fA7ygFjTSEw==
2019-05-16 21:38:02.947270-0300 MyApp[63392:1590665] decrypted: My Secret Text
My repo on GitHub with this example.

Issue with digital signature generated using Podofo library

I'm using OpenSSL to generate digital signature for a PDF by PoDoFo library.
Here is the logic for signature handler
OpenSSLSignatureHandler.h
#import <Foundation/Foundation.h>
// OpenSSL includes
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pkcs12.h>
#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#interface OpenSSLSignatureHandler : NSObject
{
SHA_CTX m_sha_ctx;
EVP_PKEY* mp_pkey; // private key
X509* mp_x509; // signing certificate
STACK_OF(X509)* mp_ca; // certificate chain up to the CA
}
- (id) initWithCert:(NSString*) p12file password: (NSString*) password;
- (void) AppendData: (NSData*)data;
- (NSData*) getSignature;
#end
OpenSSLSignatureHandler.m
#import "OpenSSLSignatureHandler.h"
#include <string>
#implementation OpenSSLSignatureHandler
- (id) initWithCert:(NSString*) p12file password: (NSString*) password{
if (self = [super init]) {
// Initialize OpenSSL library
CRYPTO_malloc_init();
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
FILE* fp = fopen([p12file cStringUsingEncoding: NSASCIIStringEncoding], "rb");
if (fp == NULL)
#throw ([NSException exceptionWithName: #"PDFNet Exception" reason: #"Cannot open private key." userInfo: nil]);
PKCS12* p12 = d2i_PKCS12_fp(fp, NULL);
fclose(fp);
if (p12 == NULL)
#throw ([NSException exceptionWithName: #"PDFNet Exception" reason: #"Cannot parse private key." userInfo: nil]);
mp_pkey = NULL;
mp_x509 = NULL;
mp_ca = NULL;
int parseResult = PKCS12_parse(p12, [password cStringUsingEncoding: NSASCIIStringEncoding], &mp_pkey, &mp_x509, &mp_ca);
PKCS12_free(p12);
if (parseResult == 0)
#throw ([NSException exceptionWithName: #"PDFNet Exception" reason: #"Cannot parse private key." userInfo: nil]);
//initialize sha context
SHA1_Init(&m_sha_ctx);
}
return self;
}
- (void) AppendData: (NSData*)data
{
SHA1_Update(&m_sha_ctx, [data bytes], [data length]);
return;
}
- (BOOL) Reset
{
SHA1_Init(&m_sha_ctx);
return (YES);
}
- (NSData*) getSignature
{
unsigned char sha_buffer[SHA_DIGEST_LENGTH];
memset((void*) sha_buffer, 0, SHA_DIGEST_LENGTH);
SHA1_Final(sha_buffer, &m_sha_ctx);
PKCS7* p7 = PKCS7_new();
PKCS7_set_type(p7, NID_pkcs7_signed);
PKCS7_SIGNER_INFO* p7Si = PKCS7_add_signature(p7, mp_x509, mp_pkey, EVP_sha1());
PKCS7_add_attrib_content_type(p7Si, OBJ_nid2obj(NID_pkcs7_data));
PKCS7_add0_attrib_signing_time(p7Si, NULL);
PKCS7_add1_attrib_digest(p7Si, (const unsigned char*) sha_buffer, SHA_DIGEST_LENGTH);
PKCS7_add_certificate(p7, mp_x509);
int c = 0;
for ( ; c < sk_X509_num(mp_ca); c++) {
X509* cert = sk_X509_value(mp_ca, c);
PKCS7_add_certificate(p7, cert);
}
PKCS7_set_detached(p7, 1);
PKCS7_content_new(p7, NID_pkcs7_data);
PKCS7_SIGNER_INFO_sign(p7Si);
int p7Len = i2d_PKCS7(p7, NULL);
NSMutableData* signature = [NSMutableData data];
unsigned char* p7Buf = (unsigned char*) malloc(p7Len);
if (p7Buf != NULL) {
unsigned char* pP7Buf = p7Buf;
i2d_PKCS7(p7, &pP7Buf);
[signature appendBytes: (const void*) p7Buf length: p7Len];
free(p7Buf);
}
PKCS7_free(p7);
return (signature);
}
- (void) dealloc
{
sk_X509_free(mp_ca);
X509_free(mp_x509);
EVP_PKEY_free(mp_pkey);
// Release OpenSSL resource usage
ERR_free_strings();
EVP_cleanup();
[super dealloc];
}
#end
Using podofo to embed signature
void CreateSimpleForm( PoDoFo::PdfPage* pPage, PoDoFo::PdfStreamedDocument* pDoc, const PoDoFo::PdfData &signatureData )
{
PoDoFo::PdfPainter painter;
PoDoFo::PdfFont* pFont = pDoc->CreateFont( "Courier" );
painter.SetPage( pPage );
painter.SetFont( pFont );
painter.DrawText( 10000 * CONVERSION_CONSTANT, 280000 * CONVERSION_CONSTANT, "PoDoFo Sign Test" );
painter.FinishPage();
PoDoFo::PdfSignatureField signField( pPage, PoDoFo::PdfRect( 0, 0, 0, 0 ), pDoc );
signField.SetFieldName("SignatureFieldName");
signField.SetSignature(signatureData);
signField.SetSignatureReason("Document verification");
// Set time of signing
signField.SetSignatureDate( PoDoFo::PdfDate() );
}
+(void)addDigitalSignatureOnPage:(NSInteger)pageIndex outpath:(NSString*)path/*doc:(PoDoFo::PdfMemDocument*)aDoc*/{
PoDoFo::PdfPage* pPage;
PoDoFo::PdfSignOutputDevice signer([path UTF8String]);
// Reserve space for signature
signer.SetSignatureSize(1024);
if([[NSFileManager defaultManager] fileExistsAtPath:path]){
PoDoFo::PdfStreamedDocument writer( &signer, PoDoFo::ePdfVersion_1_5 );
// Disable default appearance
writer.GetAcroForm(PoDoFo::ePdfCreateObject, PoDoFo::PdfAcroForm::ePdfAcroFormDefaultAppearance_None);
pPage = writer.CreatePage(PoDoFo::PdfPage::CreateStandardPageSize(PoDoFo::ePdfPageSize_A4 ) );
TEST_SAFE_OP( CreateSimpleForm( pPage, &writer, *signer.GetSignatureBeacon() ) );
TEST_SAFE_OP( writer.Close() );
}
// Check if position of signature was found
if(signer.HasSignaturePosition()) {
// Adjust ByteRange for signature
signer.AdjustByteRange();
// Read data for signature and count it
// We have to seek at the beginning of the file
signer.Seek(0);
//OpenSSLSignatureHandler
NSString * p12certpath = [[NSBundle mainBundle] pathForResource:#"iphone-cert" ofType:#"p12"];
OpenSSLSignatureHandler*signatureHandler = [[OpenSSLSignatureHandler alloc] initWithCert:p12certpath password:#"test123$"];
char buff[65536];
size_t len;
while( (len = signer.ReadForSignature(buff, 65536))>0 )
{
NSData* data = [NSData dataWithBytes:(const void *)buff length:len];
[signatureHandler AppendData:data];
}
const PoDoFo::PdfData *pSignature = NULL;
// NSString *pkcsMessage = [[signatureHandler getSignature] base64EncodedString];
// NSLog(#"OpenSSLSignatureHandler signature message = %#",pkcsMessage);
// const char * cstr = [pkcsMessage UTF8String];
// if(pSignature==NULL)pSignature = new PoDoFo::PdfData(cstr, sizeof(cstr));
unsigned char *bytePtr = (unsigned char *)[[signatureHandler getSignature] bytes];
std::string str;
str.append(reinterpret_cast<const char*>(bytePtr));
// Paste signature to the file
if(pSignature==NULL)pSignature = new PoDoFo::PdfData(str.c_str(), sizeof(str));
NSLog(#"str = %s",str.c_str());
NSLog(#"sizeof(str) = %lu",sizeof(str));
signer.SetSignature(*pSignature);
}
signer.Flush();
}
But the signature that's embeded in the PDF is always empty
can some help with this issue ?

Parsing PDF documents with Quartz

I am trying to parse a PDF document with the Quartz framework and have copy & pasted the code snippets from the Apple documentation into my source code.
Unfortunately, it does not retrieve any data. It just iterates over the pages, logs the number of the current page to the console and crashes at the end.
Do you have any idea on what is wrong with the code?
static void op_MP (CGPDFScannerRef s, void *info)
{
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
printf("MP /%s\n", name);
}
static void op_DP (CGPDFScannerRef s, void *info)
{
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
NSLog(#"DP /%s\n", name);
}
static void op_BMC (CGPDFScannerRef s, void *info)
{
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
NSLog(#"BMC /%s\n", name);
}
static void op_BDC (CGPDFScannerRef s, void *info)
{
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
NSLog(#"BDC /%s\n", name);
}
static void op_EMC (CGPDFScannerRef s, void *info)
{
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
NSLog(#"EMC /%s\n", name);
}
static void op_TJ (CGPDFScannerRef s, void *info)
{
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
NSLog(#"TJ /%s\n", name);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
CGPDFDocumentRef myDocument;
NSString *urlAddress = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"pdf"];
NSURL *fileUrl = [NSURL fileURLWithPath:urlAddress];
CFURLRef url = (__bridge CFURLRef)fileUrl;
myDocument = CGPDFDocumentCreateWithURL(url);
CFRelease (url);
if (myDocument == NULL) {// 2
NSLog(#"can't open `%#'.", fileUrl);
}
if (!CGPDFDocumentIsUnlocked (myDocument)) {// 4
CGPDFDocumentRelease(myDocument);
}
else if (CGPDFDocumentGetNumberOfPages(myDocument) == 0) {// 5
CGPDFDocumentRelease(myDocument);
}
else {
CGPDFOperatorTableRef myTable;
myTable = CGPDFOperatorTableCreate();
CGPDFOperatorTableSetCallback (myTable, "MP", &op_MP);
CGPDFOperatorTableSetCallback (myTable, "DP", &op_DP);
CGPDFOperatorTableSetCallback (myTable, "BMC", &op_BMC);
CGPDFOperatorTableSetCallback (myTable, "BDC", &op_BDC);
CGPDFOperatorTableSetCallback (myTable, "EMC", &op_EMC);
CGPDFOperatorTableSetCallback (myTable, "Tj", &op_TJ);
int k;
CGPDFPageRef myPage;
CGPDFScannerRef myScanner;
CGPDFContentStreamRef myContentStream;
int numOfPages = CGPDFDocumentGetNumberOfPages (myDocument);// 1
for (k = 0; k < numOfPages; k++) {
myPage = CGPDFDocumentGetPage (myDocument, k + 1 );// 2
myContentStream = CGPDFContentStreamCreateWithPage (myPage);// 3
myScanner = CGPDFScannerCreate (myContentStream, myTable, NULL);// 4
CGPDFScannerScan (myScanner);// 5
CGPDFPageRelease (myPage);// 6
CGPDFScannerRelease (myScanner);// 7
CGPDFContentStreamRelease (myContentStream);// 8
NSLog(#"processed page %i",k);
}
CGPDFOperatorTableRelease(myTable);
CGPDFDocumentRelease(myDocument);
}
return YES;
}
I did not run the code but the first 5 operators might not exist in your page content. Also some of them have a name operand, some of them do not have any operands (such as EMC). Also the Tj operator has a string operand, not a name.
Remove all the pop name methods and leave only the logging and you might get some output. Then look in the PDF specification to see the exact operands for each operator and update your code accordingly.
While I can't give you a solution to your example code crash, last time we needed to do this we based our parser on PDFKitten.
https://github.com/KurtCode/PDFKitten
If you are interested in the parsing code, the interesting stuff is located in Scanner.m:
https://github.com/KurtCode/PDFKitten/blob/master/PDFKitten/Scanner.m
Given the complexity of PDF parsing I would suggest working with this library as a base and moving from there. If you need a polished implementation on a deadline, then PSPDFKit is probably the most well-developed (but expensive) package.
It's about the CFRelease(url). Delete it and it will be okay.
"(__bridge T) op casts the operand to the destination type T. If T is a retainable object pointer type, then op must have a non-retainable pointer type."

Resources