| 112 | | if ( !newDocument->isOk() ) |
| 113 | | { |
| 114 | | DocumentError errorCode = (DocumentError)newDocument->getErrorCode (); |
| 115 | | delete newDocument; |
| | 94 | if ( NULL == newDocument ) |
| | 95 | { |
| | 96 | // Popller's glib wrapper passes the Poppler error code unless the |
| | 97 | // error is that the file is encrypted. We want to set our own |
| | 98 | // error code in this case. |
| | 99 | DocumentError errorCode = DocumentErrorNone; |
| | 100 | if ( POPPLER_ERROR == loadError->domain ) |
| | 101 | { |
| | 102 | errorCode = DocumentErrorEncrypted; |
| | 103 | } |
| | 104 | else |
| | 105 | { |
| | 106 | // OK, the glib's wrapper don't pass the error code directly |
| | 107 | // from Poppler. Instead returns G_FILE_ERROR_FAILED and a |
| | 108 | // non translated string. |
| | 109 | // Maybe I'm wrong (very probable) but that's a wrong way. |
| | 110 | // So I'm reading the error code from the error string... |
| | 111 | sscanf (loadError->message, "Failed to load document (error %d)", |
| | 112 | (gint *)&errorCode); |
| | 113 | } |
| | 114 | g_error_free (loadError); |
| | 115 | // Get our error message. |
| 160 | | // Retrieve the document's meta data dictionary. |
| 161 | | Object dictionary; |
| 162 | | m_Document->getDocInfo (&dictionary); |
| 163 | | |
| 164 | | gchar *value = NULL; |
| 165 | | // Author |
| 166 | | value = getDictionaryString (dictionary, "Author"); |
| 167 | | setAuthor (value); |
| 168 | | g_free (value); |
| 169 | | // Creation date. |
| 170 | | value = getDictionaryDate (dictionary, "CreationDate"); |
| 171 | | setCreationDate (value); |
| 172 | | g_free (value); |
| 173 | | // Creator |
| 174 | | value = getDictionaryString (dictionary, "Creator"); |
| 175 | | setCreator (value); |
| 176 | | g_free (value); |
| 177 | | // PDF Format |
| 178 | | value = g_strdup_printf ("PDF-%.2g", m_Document->getPDFVersion ()); |
| 179 | | setFormat (value); |
| 180 | | g_free (value); |
| 181 | | // Keywords |
| 182 | | value = getDictionaryString (dictionary, "Keywords"); |
| 183 | | setKeywords (value); |
| 184 | | g_free (value); |
| 185 | | // Is linearised |
| 186 | | setLinearized (m_Document->isLinearized ()); |
| 187 | | // Modified date |
| 188 | | value = getDictionaryDate (dictionary, "ModDate"); |
| 189 | | setModifiedDate (value); |
| 190 | | g_free (value); |
| 191 | | // Producer |
| 192 | | value = getDictionaryString (dictionary, "Producer"); |
| 193 | | setProducer (value); |
| 194 | | g_free (value); |
| 195 | | // Subject |
| 196 | | value = getDictionaryString (dictionary, "Subject"); |
| 197 | | setSubject (value); |
| 198 | | g_free (value); |
| 199 | | // Title |
| 200 | | value = getDictionaryString (dictionary, "Title"); |
| 201 | | setTitle (value); |
| 202 | | g_free (value); |
| 203 | | |
| 204 | | // For the page mode and layout we need the catalog, not the |
| 205 | | // dictionary. |
| 206 | | Catalog *catalog = m_Document->getCatalog (); |
| 207 | | setPageLayout (convertPageLayout (catalog->getPageLayout ())); |
| 208 | | setPageMode (convertPageMode (catalog->getPageMode ())); |
| | 162 | |
| | 163 | gchar *author = NULL; |
| | 164 | GTime creationDate; |
| | 165 | gchar *creator = NULL; |
| | 166 | gchar *format = NULL; |
| | 167 | gchar *keywords = NULL; |
| | 168 | PopplerPageLayout layout = POPPLER_PAGE_LAYOUT_UNSET; |
| | 169 | gchar *linearized = NULL; |
| | 170 | GTime modDate; |
| | 171 | PopplerPageMode mode = POPPLER_PAGE_MODE_UNSET; |
| | 172 | gchar *producer = NULL; |
| | 173 | gchar *subject = NULL; |
| | 174 | gchar *title = NULL; |
| | 175 | |
| | 176 | g_object_get (m_Document, |
| | 177 | "author", &author, |
| | 178 | "creation-date", &creationDate, |
| | 179 | "creator", &creator, |
| | 180 | "format", &format, |
| | 181 | "keywords", &keywords, |
| | 182 | "page-layout", &layout, |
| | 183 | "linearized", &linearized, |
| | 184 | "mod-date", &modDate, |
| | 185 | "page-mode", &mode, |
| | 186 | "producer", &producer, |
| | 187 | "subject", &subject, |
| | 188 | "title", &title, |
| | 189 | NULL); |
| | 190 | setAuthor (author); |
| | 191 | if ( 0 < creationDate ) |
| | 192 | { |
| | 193 | struct tm *tmpTime = localtime ((const time_t *)&creationDate); |
| | 194 | gchar *date = g_strnfill (DATE_LENGTH + 1, 0); |
| | 195 | strftime (date, DATE_LENGTH, "%Y-%m-%d %H:%M:%S", tmpTime); |
| | 196 | setCreationDate (date); |
| | 197 | } |
| | 198 | else |
| | 199 | { |
| | 200 | setCreationDate (NULL); |
| | 201 | } |
| | 202 | setCreator (creator); |
| | 203 | setFormat (format); |
| | 204 | setKeywords (keywords); |
| | 205 | setLinearized (linearized); |
| | 206 | if ( 0 < modDate ) |
| | 207 | { |
| | 208 | struct tm *tmpTime = localtime ((const time_t *)&modDate); |
| | 209 | gchar *date = g_strnfill (DATE_LENGTH + 1, 0); |
| | 210 | strftime (date, DATE_LENGTH, "%Y-%m-%d %H:%M:%S", tmpTime); |
| | 211 | setModifiedDate (date); |
| | 212 | } |
| | 213 | else |
| | 214 | { |
| | 215 | setModifiedDate (NULL); |
| | 216 | } |
| | 217 | setProducer (producer); |
| | 218 | setSubject (subject); |
| | 219 | setTitle (title); |
| | 220 | |
| | 221 | // For the page mode and layout we need the enumerator value |
| | 222 | GEnumValue *pageLayout = g_enum_get_value ( |
| | 223 | (GEnumClass *)g_type_class_peek (POPPLER_TYPE_PAGE_LAYOUT), layout); |
| | 224 | setPageLayout (convertPageLayout (pageLayout->value)); |
| | 225 | GEnumValue *pageMode = g_enum_get_value ( |
| | 226 | (GEnumClass *)g_type_class_peek (POPPLER_TYPE_PAGE_MODE), mode); |
| | 227 | setPageMode (convertPageMode (pageMode->value)); |
| 251 | | LinkDest *linkDestination = NULL; |
| 252 | | GooString *namedDest = ((LinkGoTo *)action)->getNamedDest (); |
| 253 | | // getNamedDest() or getDest() will return NULL, just need to |
| 254 | | // find which one. |
| 255 | | if ( NULL != namedDest ) |
| 256 | | { |
| 257 | | linkDestination = m_Document->findDest (namedDest); |
| 258 | | } |
| 259 | | else |
| 260 | | { |
| 261 | | linkDestination = ((LinkGoTo *)action)->getDest (); |
| 262 | | } |
| 263 | | if ( NULL != linkDestination && linkDestination->isOk () ) |
| 264 | | { |
| 265 | | if ( linkDestination->isPageRef () ) |
| 266 | | { |
| 267 | | Ref pageReference = linkDestination->getPageRef (); |
| 268 | | destination = m_Document->findPage (pageReference.num, |
| 269 | | pageReference.gen); |
| 270 | | } |
| 271 | | else |
| 272 | | { |
| 273 | | destination = linkDestination->getPageNum (); |
| 274 | | } |
| 275 | | } // if ( linkDestination->isOk () ) |
| 276 | | } // if ( NULL != action ... ) |
| 277 | | child->setDestination (destination); |
| 278 | | outline->addChild (child); |
| 279 | | // Add the child's children, if any. |
| 280 | | if ( item->hasKids () ) |
| 281 | | { |
| 282 | | // I need to open the outline because if it's not |
| 283 | | // open then the getKids() function would return NULL. |
| 284 | | item->open (); |
| 285 | | setOutline (child, item->getKids ()); |
| | 258 | PopplerActionGotoDest *actionGoTo = |
| | 259 | (PopplerActionGotoDest *)action; |
| | 260 | DocumentOutline *child = new DocumentOutline (); |
| | 261 | child->setParent (outline); |
| | 262 | child->setTitle (actionGoTo->title); |
| | 263 | PopplerDest *destination = actionGoTo->dest; |
| | 264 | child->setDestination (destination->page_num); |
| | 265 | outline->addChild (child); |
| | 266 | PopplerIndexIter *childIter = |
| | 267 | poppler_index_iter_get_child (childrenList); |
| | 268 | setOutline (child, childIter); |
| 287 | | } // for ( int childIndex = 0 ; ... ) |
| 288 | | } // if ( NULL != childrenList ) |
| 289 | | } |
| 290 | | |
| 291 | | /// |
| 292 | | /// @brief Gets the string value from a document's dictionary. |
| 293 | | /// |
| 294 | | /// Retrieves the string value of @a key inside the dictionary @a directory. The |
| 295 | | /// dictionary must be an Object retrieved by calling Poppler's getDocInfo() |
| 296 | | /// function. |
| 297 | | /// |
| 298 | | /// @param object The object retrieved by calling Poppler's getDocInfo(). |
| 299 | | /// @param key The key's name to retrieve it's value. |
| 300 | | /// |
| 301 | | /// @return The string value of @a key or an empty string if it couldn't |
| 302 | | /// be retrieved. |
| 303 | | /// The returned string must be freed by calling g_free(). |
| 304 | | /// |
| 305 | | gchar * |
| 306 | | getDictionaryString (Object &object, const gchar *key) |
| 307 | | { |
| 308 | | gchar *result = NULL; |
| 309 | | if (object.isDict ()) |
| 310 | | { |
| 311 | | Dict *dictionary = object.getDict (); |
| 312 | | Object value; |
| 313 | | if ( !dictionary->lookup ((gchar *)key, &value)->isString () ) |
| 314 | | { |
| 315 | | value.free (); |
| 316 | | result = g_strdup (""); |
| 317 | | } |
| 318 | | else |
| 319 | | { |
| 320 | | GooString *value_g = value.getString (); |
| 321 | | if ( hasUnicodeMarker (value_g) ) |
| 322 | | { |
| 323 | | // Convert the string skipping the Unicode marker, |
| 324 | | // which is 2 bytes long (UTF-16BE). |
| 325 | | result = g_convert (value_g->getCString () + 2, |
| 326 | | value_g->getLength () - 2, |
| 327 | | "UTF-8", "UTF-16BE", NULL, NULL, NULL); |
| 328 | | } |
| 329 | | else |
| 330 | | { |
| 331 | | // Otherwise we need to get each character in the document's |
| 332 | | // encoding, transform it to UCS4 and finally transform to |
| 333 | | // UTF-8. |
| 334 | | int stringLength = value_g->getLength (); |
| 335 | | gunichar *ucs4_tmp = g_new (gunichar, stringLength + 1); |
| 336 | | for ( int pos = 0 ; pos < stringLength ; pos++) |
| 337 | | { |
| 338 | | ucs4_tmp[pos] = |
| 339 | | pdfDocEncoding[(unsigned char)value_g->getChar(pos)]; |
| 340 | | } |
| 341 | | ucs4_tmp[stringLength] = 0; |
| 342 | | result = g_ucs4_to_utf8 (ucs4_tmp, -1, NULL, NULL, NULL); |
| 343 | | } |
| 344 | | value.free (); |
| 345 | | } |
| 346 | | } |
| 347 | | else |
| 348 | | { |
| 349 | | result = g_strdup (""); |
| 350 | | } |
| 351 | | |
| 352 | | g_assert (NULL != result && "The result is NULL!"); |
| 353 | | return result; |
| 354 | | } |
| 355 | | |
| 356 | | /// |
| 357 | | /// @brief Gets the date value from a document's dictionary. |
| 358 | | /// |
| 359 | | /// Retrieves the string value of @a key inside the dictionary @a directory. The |
| 360 | | /// dictionary must be an Object retrieved by calling Poppler's getDocInfo() |
| 361 | | /// function. |
| 362 | | /// |
| 363 | | /// @param object The object retrieved by calling Poppler's getDocInfo(). |
| 364 | | /// @param key The key's name to retrieve it's value. |
| 365 | | /// |
| 366 | | /// @return The string value of @a key or an empty string if it couldn't |
| 367 | | /// be retrieved. |
| 368 | | /// The returned string must be freed by calling g_free(). |
| 369 | | /// |
| 370 | | gchar * |
| 371 | | getDictionaryDate (Object &object, const gchar *key) |
| 372 | | { |
| 373 | | gchar *result = NULL; |
| 374 | | if ( object.isDict () ) |
| 375 | | { |
| 376 | | Dict *dictionary = object.getDict (); |
| 377 | | Object value; |
| 378 | | if ( !dictionary->lookup ((gchar *)key, &value)->isString () ) |
| 379 | | { |
| 380 | | value.free (); |
| 381 | | result = g_strdup (""); |
| 382 | | } |
| 383 | | else |
| 384 | | { |
| 385 | | GooString *value_g = value.getString (); |
| 386 | | gchar *dateString = NULL; |
| 387 | | if ( hasUnicodeMarker (value_g) ) |
| 388 | | { |
| 389 | | // Copy the string skipping the two-bytes long |
| 390 | | // Unicode marker. |
| 391 | | dateString = g_convert (value_g->getCString () + 2, |
| 392 | | value_g->getLength () - 2, |
| 393 | | "UTF-8", "UTF-16BE", NULL, NULL, NULL); |
| 394 | | } |
| 395 | | else |
| 396 | | { |
| 397 | | dateString = g_strndup (value_g->getCString (), |
| 398 | | value_g->getLength ()); |
| 399 | | } |
| 400 | | value.free (); |
| 401 | | |
| 402 | | // Set PDF Reference 1.3, Section 3.8.2 for PDF Date representation. |
| 403 | | gchar *oldDate = dateString; |
| 404 | | if ( 'D' == dateString[0] && ':' == dateString[1] ) |
| 405 | | { |
| 406 | | dateString += 2; |
| 407 | | } |
| 408 | | int year; |
| 409 | | int month; |
| 410 | | int day; |
| 411 | | int hour; |
| 412 | | int minute; |
| 413 | | int second; |
| 414 | | |
| 415 | | int scannedItems = sscanf (dateString, "%4d%2d%2d%2d%2d%2d", |
| 416 | | &year, &month, &day, &hour, &minute, &second); |
| 417 | | g_free (oldDate); |
| 418 | | if ( 6 != scannedItems ) |
| 419 | | { |
| 420 | | result = g_strdup (""); |
| 421 | | } |
| 422 | | else |
| 423 | | { |
| 424 | | result = g_strdup_printf ("%04d-%02d-%02d %02d:%02d:%02d", |
| 425 | | year, month, day, hour, minute, |
| 426 | | second); |
| 427 | | } |
| 428 | | } |
| 429 | | } |
| 430 | | else |
| 431 | | { |
| 432 | | result = g_strdup (""); |
| 433 | | } |
| 434 | | g_assert ( result != NULL && "The result is NULL."); |
| 435 | | return result; |
| 436 | | } |
| 437 | | |
| 438 | | /// |
| 439 | | /// @brief Checks if a GooString has the Unicode marker. |
| 440 | | /// |
| 441 | | /// I really don't know, but it seems that some PDF files has UTF-16BE |
| 442 | | /// strings. This function just tests if the string has this BOM (Binary |
| 443 | | /// Order Marker), which is 0xfeff for Big Endian and 0xfffe for Little Endian. |
| 444 | | /// |
| 445 | | /// @param string The string to check if has the Unicode marker. |
| 446 | | /// |
| 447 | | /// @return TRUE if the GooString has the Unicode marker, FALSE otherwise. |
| 448 | | /// |
| 449 | | gboolean |
| 450 | | hasUnicodeMarker (GooString *string) |
| 451 | | { |
| 452 | | return ((string->getChar (0) & 0xff) == 0xfe && |
| 453 | | (string->getChar (1) & 0xff) == 0xff ); |
| 454 | | } |
| 455 | | |
| 456 | | /// |
| 457 | | /// @brief Gets the document's page layout from Poppler's catalog. |
| 458 | | /// |
| 459 | | /// @param pageLayout Is the page layout that Poppler's catalog gives. |
| | 270 | } |
| | 271 | while ( poppler_index_iter_next (childrenList) ); |
| | 272 | |
| | 273 | poppler_index_iter_free (childrenList); |
| | 274 | } |
| | 275 | } |
| | 276 | |
| | 277 | /// |
| | 278 | /// @brief Gets the document's page layout from Poppler's page layout. |
| | 279 | /// |
| | 280 | /// @param pageLayout Is the page layout that Poppler's glib wrapper gives. |