Как этот CGPDFDictionaryGetString переводить в Monotouch?

У меня есть кусок кода ObjC от великого VFR PDF Viewer на GIT. Он использует CGPDFDictionaryGetString получить указатель на строку из аннотации PDF. Затем он использует преобразование байтового указателя для получения окончательной строки. В монотуше нет CGPDFDictionary.GetString() но только .GetName() - это единственный метод, который возвращает строку, поэтому я предположил, что это должен быть правильный метод, но он не работает. Я могу получить массивы, словари, числа с плавающей запятой и целые числа просто отлично - кажется, что только строки не работают.

Посмотрите небольшие примеры кода ниже.

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.
}

Я перевел это так:

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.
}

РЕДАКТИРОВАТЬ: Глядя на источники Mono, я вижу это в ветке Master:// TODO: GetString -> возвращает CGPDFString

Переключение на ветку 4.2 показывает, что она там есть. Поэтому я скопировал код оттуда, но у меня есть две проблемы:

  • Я получаю сообщение об ошибке "небезопасное" ключевое слово. Он говорит мне, чтобы добавить "небезопасную" опцию командной строки. Что это такое, и стоит ли добавлять его? Куда?
  • Кажется, что все равно работает, но приложение зависает при получении 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;
    }

2 ответа

Решение

Если вы используете ключевое слово unsafe в своем источнике, вам необходимо включить unsafe при сборке сборки. В MonoDevelop вы можете сделать это:

  • Щелкните правой кнопкой мыши на проекте;
  • Выберите меню " Параметры";
  • Выберите значок " Общие";
  • Установите флажок Разрешить "небезопасный" код
  • Нажмите кнопку ОК
  • Затем восстановите.

Примечание. Ваша предыдущая сборка не работала бы без этого.

Исходный код между master и monotouch-4.2 должен быть идентичен в этом случае. Я проверю, но, вероятно, вы просматривали конкретную ревизию в GIT (она была передана до обновления кода). Я обязательно проверю и отредактирую пост.

ОБНОВЛЕНИЕ: Это ссылка на мастер (т.е. последний доступный код), и он показывает:

public bool GetString (string key, out string result)

быть доступным. Однако это зависит от небезопасного кода (внутри PdfStringToString), который вы не должны были компилировать, не допуская небезопасного кода в сборке, в которую вы копировали / вставляли этот код.

UPDATE2: возвращаемое значение имеет кодировку UTF8, поэтому созданная из него строка должна быть правильно декодирована (другой конструктор System.String позволяет это). Приведенная выше ссылка на мастер должна указывать на фиксированную версию.

Я не большой поклонник использования небезопасных блоков и разработал способ реализации этого метода без его использования. Первоначально я попробовал небезопасный стиль, однако, поскольку строка хранится в UTF8, ее необходимо преобразовать.

private bool PDFDictionaryGetString (IntPtr handle, string key, out string result)
{
    IntPtr stringPtr;
    result = null;

    if (CGPDFDictionaryGetString(handle, "URI", out stringPtr)) {

        if (stringPtr == IntPtr.Zero)
                return false;

        // Get length of PDF String
        uint n = (uint) CGPDFStringGetLength (stringPtr);

        // Get the pointer of the string
        var ptr = CGPDFStringGetBytePtr (stringPtr);
        // Get the bytes
        var data = NSData.FromBytes(ptr, n);
        // Convert to UTF8
        var value = NSString.FromData(data, NSStringEncoding.UTF8);

        result = value.ToString();
        return true;
    }
    return false;
} 

Здесь есть полный пост в блоге и рабочий образец, включая полный исходный код, включающий многостраничную навигацию и щелкающие ссылки.

Другие вопросы по тегам