Search code examples
c#pdfxamarin.iospinvokecgpdfdictionaryref

How does this CGPDFDictionaryGetString translate into Monotouch?


I have a piece of ObjC code from the great VFR PDF Viewer on GIT. It is using CGPDFDictionaryGetString to get a pointer to a string from a PDF annotation. Then it uses some byte pointer conversion to get the final string. In Monotouch there is no CGPDFDictionary.GetString() but only a .GetName() - this is the only method that returns a string, so I assumed that must be the correct method but it does not work. I can retrieve arrays, dictionaries, floats and integers just fine - only the strings don't seem to work.

See the small code examples below.

CGPDFStringRef uriString = NULL;
// This returns TRUE in the ObjC version and uriString is a valid pointer to a string.
if (CGPDFDictionaryGetString(actionDictionary, "URI", &uriString) == true)
{
  // Do some pointer magic - how to do this in MT? Do I have to at all?
  const char *uri = (const char *)CGPDFStringGetBytePtr(uriString);
  // *uri now contains a URL, I can see it in the debugger.
}

I translated it like that:

string sUri = null;
// This returns FALSE. Hence my sUri is NULL. Seems like GetName() is not the analogy to CGPDFDictionaryGetString.
if(oActionDic.GetName("URI", out sUri))
{
  // I never get here.
}

EDIT: Looking at the Mono sources I can see this in the Master branch: // TODO: GetString -> returns a CGPDFString

Switching to branch 4.2 reveals that it seems to be there. So I copied the code from there but have two issues:

  • I get an error about the "unsafe" keyword. It tells me to add the "unsafe" command line option. What is that and is it a good idea to add it? Where?
  • It seems to run anyway but the app hangs when getting the CGPDFString.

[DllImport (Constants.CoreGraphicsLibrary)] public extern static IntPtr CGPDFStringGetLength (IntPtr pdfStr);

    [DllImport (Constants.CoreGraphicsLibrary)]
    public extern static IntPtr CGPDFStringGetBytePtr (IntPtr pdfStr);

    public static string PdfStringToString (IntPtr pdfString)
    {
        if (pdfString == IntPtr.Zero)
            return null;

        int n = (int)CGPDFStringGetLength (pdfString);
        unsafe
        {
            return new String ((char *)CGPDFStringGetBytePtr (pdfString), 0, n);
        }
    }

    [DllImport (Constants.CoreGraphicsLibrary)]
    extern static bool CGPDFDictionaryGetString (IntPtr handle, string key, out IntPtr result);

    public static bool GetStringFromPdfDictionary (CGPDFDictionary oPdfDic, string key, out string result)
    {
        if (key == null)
            throw new ArgumentNullException ("key");
        IntPtr res;
        if (CGPDFDictionaryGetString (oPdfDic.Handle, key, out res))
        {
            result = PdfStringToString (res);
            return true;
        }
        result = null;
        return false;
    }

Solution

  • If you use the unsafe keyword in your source then you need to enable unsafe when building your assembly. In MonoDevelop you can do this by:

    • Right-click on the project;
    • Select Options menu;
    • Select General icon;
    • Click the Allow 'unsafe' code checkbox
    • Click Ok button
    • Then rebuild.

    Note: Your previous build should not have worked without this.

    Source code between master and monotouch-4.2 should be identical in this case. I'll check but it's likely that you were looking at a specific revision in GIT (the was pushed before the code was updated). I'll check to be sure and edit the post.

    UPDATE : This is the link to master (i.e. latest code available) and it shows:

    public bool GetString (string key, out string result)
    

    to be available. However it does depend on unsafe code (inside PdfStringToString) which you should not have been able to compile without allowing unsafe code in the assembly where you copy/pasted this code.

    UPDATE2 : The value returned is UTF8 encoded so the string created from it needs to be decoded properly (another System.String constructor allows this). The above link to master should already point to the fixed version.