Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/helpers/mu-office-lib/mu-office-lib.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 // Copyright (C) 2004-2025 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 /** | |
| 24 * Mu Office Library | |
| 25 * | |
| 26 * Provided access to the core document, loading, displaying and | |
| 27 * editing routines | |
| 28 * | |
| 29 * Intended for use with native UI | |
| 30 */ | |
| 31 | |
| 32 #include "mupdf/fitz.h" | |
| 33 #include "mupdf/pdf.h" | |
| 34 #include "mupdf/helpers/mu-office-lib.h" | |
| 35 #include "mupdf/helpers/mu-threads.h" | |
| 36 #include "mupdf/memento.h" | |
| 37 | |
| 38 #include <assert.h> | |
| 39 | |
| 40 enum | |
| 41 { | |
| 42 MuError_OK = 0, | |
| 43 MuError_OOM = -1, | |
| 44 MuError_BadNull = -2, | |
| 45 MuError_Generic = -3, | |
| 46 MuError_NotImplemented = -4, | |
| 47 MuError_PasswordPending = -5, | |
| 48 }; | |
| 49 | |
| 50 enum { | |
| 51 LAYOUT_W = 450, | |
| 52 LAYOUT_H = 600, | |
| 53 LAYOUT_EM = 12 | |
| 54 }; | |
| 55 | |
| 56 #ifdef DISABLE_MUTHREADS | |
| 57 #error "mu-office-lib requires threading to be enabled" | |
| 58 #endif | |
| 59 | |
| 60 /* | |
| 61 If we are building as part of a smartoffice build, then we | |
| 62 should appeal to Pal_Mem_etc to get memory. If not, then | |
| 63 we should use malloc instead. | |
| 64 | |
| 65 FIXME: Allow for something other than malloc/calloc/realloc/ | |
| 66 free here. | |
| 67 */ | |
| 68 #ifndef SMARTOFFICE_BUILD | |
| 69 void *Pal_Mem_calloc(unsigned int num, size_t size) | |
| 70 { | |
| 71 return calloc(num, size); | |
| 72 } | |
| 73 | |
| 74 void *Pal_Mem_malloc(size_t size) | |
| 75 { | |
| 76 return malloc(size); | |
| 77 } | |
| 78 | |
| 79 void *Pal_Mem_realloc(void *ptr, size_t size) | |
| 80 { | |
| 81 return realloc(ptr, size); | |
| 82 } | |
| 83 | |
| 84 void Pal_Mem_free(void *address) | |
| 85 { | |
| 86 free(address); | |
| 87 } | |
| 88 #endif | |
| 89 | |
| 90 /* | |
| 91 All MuPDF's allocations are redirected through the | |
| 92 following functions. | |
| 93 */ | |
| 94 static void *muoffice_malloc(void *arg, size_t size) | |
| 95 { | |
| 96 return Pal_Mem_malloc(size); | |
| 97 } | |
| 98 | |
| 99 static void *muoffice_realloc(void *arg, void *old, size_t size) | |
| 100 { | |
| 101 return Pal_Mem_realloc(old, size); | |
| 102 } | |
| 103 | |
| 104 static void muoffice_free(void *arg, void *ptr) | |
| 105 { | |
| 106 Pal_Mem_free(ptr); | |
| 107 } | |
| 108 | |
| 109 static fz_alloc_context muoffice_alloc = | |
| 110 { | |
| 111 /* user */ | |
| 112 NULL, | |
| 113 | |
| 114 /* void *(*malloc)(void *, size_t); */ | |
| 115 muoffice_malloc, | |
| 116 | |
| 117 /* void *(*realloc)(void *, void *, size_t); */ | |
| 118 muoffice_realloc, | |
| 119 | |
| 120 /* void (*free)(void *, void *); */ | |
| 121 muoffice_free | |
| 122 }; | |
| 123 | |
| 124 /* | |
| 125 All MuPDF's locking is done using the following functions | |
| 126 */ | |
| 127 static void muoffice_lock(void *user, int lock); | |
| 128 | |
| 129 static void muoffice_unlock(void *user, int lock); | |
| 130 | |
| 131 struct MuOfficeLib | |
| 132 { | |
| 133 fz_context *ctx; | |
| 134 mu_mutex mutexes[FZ_LOCK_MAX+1]; | |
| 135 fz_locks_context locks; | |
| 136 }; | |
| 137 | |
| 138 /* | |
| 139 We add 1 extra lock which we use in this helper to protect | |
| 140 against accessing the fz_document from multiple threads | |
| 141 inadvertently when the caller is calling 'run' or | |
| 142 'runBackground'. | |
| 143 */ | |
| 144 enum | |
| 145 { | |
| 146 DOCLOCK = FZ_LOCK_MAX | |
| 147 }; | |
| 148 | |
| 149 static void muoffice_lock(void *user, int lock) | |
| 150 { | |
| 151 MuOfficeLib *mu = (MuOfficeLib *)user; | |
| 152 | |
| 153 mu_lock_mutex(&mu->mutexes[lock]); | |
| 154 } | |
| 155 | |
| 156 static void muoffice_unlock(void *user, int lock) | |
| 157 { | |
| 158 MuOfficeLib *mu = (MuOfficeLib *)user; | |
| 159 | |
| 160 mu_unlock_mutex(&mu->mutexes[lock]); | |
| 161 } | |
| 162 | |
| 163 static void muoffice_doc_lock(MuOfficeLib *mu) | |
| 164 { | |
| 165 mu_lock_mutex(&mu->mutexes[DOCLOCK]); | |
| 166 } | |
| 167 | |
| 168 static void muoffice_doc_unlock(MuOfficeLib *mu) | |
| 169 { | |
| 170 mu_unlock_mutex(&mu->mutexes[DOCLOCK]); | |
| 171 } | |
| 172 | |
| 173 static void fin_muoffice_locks(MuOfficeLib *mu) | |
| 174 { | |
| 175 int i; | |
| 176 | |
| 177 for (i = 0; i < FZ_LOCK_MAX+1; i++) | |
| 178 mu_destroy_mutex(&mu->mutexes[i]); | |
| 179 } | |
| 180 | |
| 181 static fz_locks_context *init_muoffice_locks(MuOfficeLib *mu) | |
| 182 { | |
| 183 int i; | |
| 184 int failed = 0; | |
| 185 | |
| 186 for (i = 0; i < FZ_LOCK_MAX+1; i++) | |
| 187 failed |= mu_create_mutex(&mu->mutexes[i]); | |
| 188 | |
| 189 if (failed) | |
| 190 { | |
| 191 fin_muoffice_locks(mu); | |
| 192 return NULL; | |
| 193 } | |
| 194 | |
| 195 mu->locks.user = mu; | |
| 196 mu->locks.lock = muoffice_lock; | |
| 197 mu->locks.unlock = muoffice_unlock; | |
| 198 | |
| 199 return &mu->locks; | |
| 200 } | |
| 201 | |
| 202 MuError MuOfficeLib_create(MuOfficeLib **pMu) | |
| 203 { | |
| 204 MuOfficeLib *mu; | |
| 205 fz_locks_context *locks; | |
| 206 | |
| 207 if (pMu == NULL) | |
| 208 return MuOfficeDocErrorType_IllegalArgument; | |
| 209 | |
| 210 mu = Pal_Mem_calloc(1, sizeof(MuOfficeLib)); | |
| 211 if (mu == NULL) | |
| 212 return MuOfficeDocErrorType_OutOfMemory; | |
| 213 | |
| 214 locks = init_muoffice_locks(mu); | |
| 215 if (locks == NULL) | |
| 216 goto Fail; | |
| 217 | |
| 218 mu->ctx = fz_new_context(&muoffice_alloc, locks, FZ_STORE_DEFAULT); | |
| 219 if (mu->ctx == NULL) | |
| 220 goto Fail; | |
| 221 | |
| 222 fz_try(mu->ctx) | |
| 223 fz_register_document_handlers(mu->ctx); | |
| 224 fz_catch(mu->ctx) | |
| 225 goto Fail; | |
| 226 | |
| 227 *pMu = mu; | |
| 228 | |
| 229 return MuOfficeDocErrorType_NoError; | |
| 230 | |
| 231 Fail: | |
| 232 if (mu) | |
| 233 { | |
| 234 fin_muoffice_locks(mu); | |
| 235 Pal_Mem_free(mu); | |
| 236 } | |
| 237 return MuOfficeDocErrorType_OutOfMemory; | |
| 238 } | |
| 239 | |
| 240 /** | |
| 241 * Destroy a MuOfficeLib instance | |
| 242 * | |
| 243 * @param mu the instance to destroy | |
| 244 */ | |
| 245 void MuOfficeLib_destroy(MuOfficeLib *mu) | |
| 246 { | |
| 247 if (mu == NULL) | |
| 248 return; | |
| 249 | |
| 250 fz_drop_context(mu->ctx); | |
| 251 fin_muoffice_locks(mu); | |
| 252 | |
| 253 Pal_Mem_free(mu); | |
| 254 } | |
| 255 | |
| 256 /** | |
| 257 * Perform MuPDF native operations on a given MuOfficeLib | |
| 258 * instance. | |
| 259 * | |
| 260 * The function is called with a fz_context value that can | |
| 261 * be safely used (i.e. the context is cloned/dropped | |
| 262 * appropriately around the call). The function should signal | |
| 263 * errors by fz_throw-ing. | |
| 264 * | |
| 265 * @param mu the MuOfficeLib instance. | |
| 266 * @param fn the function to call to run the operations. | |
| 267 * @param arg Opaque data pointer. | |
| 268 * | |
| 269 * @return error indication - 0 for success | |
| 270 */ | |
| 271 MuError MuOfficeLib_run(MuOfficeLib *mu, void (*fn)(fz_context *ctx, void *arg), void *arg) | |
| 272 { | |
| 273 fz_context *ctx; | |
| 274 MuError err = MuError_OK; | |
| 275 | |
| 276 if (mu == NULL) | |
| 277 return MuError_BadNull; | |
| 278 if (fn == NULL) | |
| 279 return err; | |
| 280 | |
| 281 ctx = fz_clone_context(mu->ctx); | |
| 282 if (ctx == NULL) | |
| 283 return MuError_OOM; | |
| 284 | |
| 285 fz_try(ctx) | |
| 286 fn(ctx, arg); | |
| 287 fz_catch(ctx) | |
| 288 err = MuError_Generic; | |
| 289 | |
| 290 fz_drop_context(ctx); | |
| 291 | |
| 292 return err; | |
| 293 } | |
| 294 | |
| 295 /** | |
| 296 * Find the type of a file given its filename extension. | |
| 297 * | |
| 298 * @param path path to the file (in utf8) | |
| 299 * | |
| 300 * @return a valid MuOfficeDocType value, or MuOfficeDocType_Other | |
| 301 */ | |
| 302 MuOfficeDocType MuOfficeLib_getDocTypeFromFileExtension(const char *path) | |
| 303 { | |
| 304 return /* FIXME */MuOfficeDocType_PDF; | |
| 305 } | |
| 306 | |
| 307 /** | |
| 308 * Return a list of file extensions supported by Mu Office library. | |
| 309 * | |
| 310 * @return comma-delimited list of extensions, without the leading ".". | |
| 311 * The caller should free the returned pointer.. | |
| 312 */ | |
| 313 char * MuOfficeLib_getSupportedFileExtensions(void) | |
| 314 { | |
| 315 /* FIXME */ | |
| 316 return NULL; | |
| 317 } | |
| 318 | |
| 319 struct MuOfficeDoc | |
| 320 { | |
| 321 MuOfficeLib *mu; | |
| 322 fz_context *ctx; | |
| 323 MuOfficeLoadingProgressFn *progress; | |
| 324 MuOfficeLoadingErrorFn *error; | |
| 325 void *cookie; | |
| 326 char *path; | |
| 327 char *password; | |
| 328 mu_semaphore password_sem; | |
| 329 mu_thread thread; | |
| 330 int needs_password; | |
| 331 int aborted; | |
| 332 fz_document *doc; | |
| 333 | |
| 334 MuOfficePage *pages; | |
| 335 }; | |
| 336 | |
| 337 struct MuOfficePage | |
| 338 { | |
| 339 MuOfficePage *next; | |
| 340 MuOfficeDoc *doc; | |
| 341 int pageNum; | |
| 342 void *cookie; | |
| 343 MuOfficePageUpdateFn *updateFn; | |
| 344 fz_page *page; | |
| 345 fz_display_list *list; | |
| 346 }; | |
| 347 | |
| 348 struct MuOfficeRender | |
| 349 { | |
| 350 MuOfficePage *page; | |
| 351 float zoom; | |
| 352 const MuOfficeBitmap *bitmap; | |
| 353 int area_valid; | |
| 354 MuOfficeRenderArea area; | |
| 355 MuOfficeRenderProgressFn *progress; | |
| 356 MuError error; | |
| 357 mu_thread thread; | |
| 358 void *cookie; | |
| 359 fz_cookie mu_cookie; | |
| 360 }; | |
| 361 | |
| 362 static void load_worker(void *arg) | |
| 363 { | |
| 364 MuOfficeDoc *doc = (MuOfficeDoc *)arg; | |
| 365 int numPages = 0; | |
| 366 fz_context *ctx = fz_clone_context(doc->ctx); | |
| 367 int err = 0; | |
| 368 | |
| 369 if (ctx == NULL) | |
| 370 { | |
| 371 return; | |
| 372 } | |
| 373 | |
| 374 muoffice_doc_lock(doc->mu); | |
| 375 | |
| 376 fz_try(ctx) | |
| 377 { | |
| 378 doc->doc = fz_open_document(ctx, doc->path); | |
| 379 doc->needs_password = fz_needs_password(ctx, doc->doc); | |
| 380 } | |
| 381 fz_catch(ctx) | |
| 382 { | |
| 383 err = MuOfficeDocErrorType_UnsupportedDocumentType; | |
| 384 goto fail; | |
| 385 } | |
| 386 | |
| 387 fz_try(ctx) | |
| 388 { | |
| 389 if (doc->needs_password && doc->error) | |
| 390 { | |
| 391 do | |
| 392 { | |
| 393 doc->error(doc->cookie, MuOfficeDocErrorType_PasswordRequest); | |
| 394 mu_wait_semaphore(&doc->password_sem); | |
| 395 if (doc->aborted) | |
| 396 break; | |
| 397 doc->needs_password = (fz_authenticate_password(ctx, doc->doc, doc->password) != 0); | |
| 398 Pal_Mem_free(doc->password); | |
| 399 doc->password = NULL; | |
| 400 } | |
| 401 while (doc->needs_password); | |
| 402 } | |
| 403 | |
| 404 fz_layout_document(ctx, doc->doc, LAYOUT_W, LAYOUT_H, LAYOUT_EM); | |
| 405 | |
| 406 numPages = fz_count_pages(ctx, doc->doc); | |
| 407 } | |
| 408 fz_catch(ctx) | |
| 409 err = MuOfficeDocErrorType_UnableToLoadDocument; | |
| 410 | |
| 411 fail: | |
| 412 muoffice_doc_unlock(doc->mu); | |
| 413 | |
| 414 if (err) | |
| 415 doc->error(doc->cookie, err); | |
| 416 | |
| 417 if (doc->progress) | |
| 418 doc->progress(doc->cookie, numPages, 1); | |
| 419 | |
| 420 fz_drop_context(ctx); | |
| 421 } | |
| 422 | |
| 423 /** | |
| 424 * Load a document | |
| 425 * | |
| 426 * Call will return immediately, leaving the document loading | |
| 427 * in the background | |
| 428 * | |
| 429 * @param so a MuOfficeLib instance | |
| 430 * @param path path to the file to load (in utf8) | |
| 431 * @param progressFn callback for monitoring progress | |
| 432 * @param errorFn callback for monitoring errors | |
| 433 * @param cookie a pointer to pass back to the callbacks | |
| 434 * @param pDoc address for return of a MuOfficeDoc object | |
| 435 * | |
| 436 * @return error indication - 0 for success | |
| 437 * | |
| 438 * The progress callback may be called several times, with increasing | |
| 439 * values of pagesLoaded. Unless MuOfficeDoc_destroy is called, | |
| 440 * before loading completes, a call with "completed" set to true | |
| 441 * is guaranteed. | |
| 442 * | |
| 443 * Once MuOfficeDoc_destroy is called there will be no | |
| 444 * further callbacks. | |
| 445 * | |
| 446 * Alternatively, in a synchronous context, MuOfficeDoc_getNumPages | |
| 447 * can be called to wait for loading to complete and return the total | |
| 448 * number of pages. In this mode of use, progressFn can be NULL. | |
| 449 */ | |
| 450 MuError MuOfficeLib_loadDocument( MuOfficeLib *mu, | |
| 451 const char *path, | |
| 452 MuOfficeLoadingProgressFn *progressFn, | |
| 453 MuOfficeLoadingErrorFn *errorFn, | |
| 454 void *cookie, | |
| 455 MuOfficeDoc **pDoc) | |
| 456 { | |
| 457 MuOfficeDoc *doc; | |
| 458 fz_context *ctx; | |
| 459 | |
| 460 if (mu == NULL || pDoc == NULL) | |
| 461 return MuOfficeDocErrorType_IllegalArgument; | |
| 462 | |
| 463 *pDoc = NULL; | |
| 464 | |
| 465 doc = Pal_Mem_calloc(1, sizeof(*doc)); | |
| 466 if (doc == NULL) | |
| 467 return MuOfficeDocErrorType_NoError; | |
| 468 | |
| 469 ctx = mu->ctx; | |
| 470 doc->mu = mu; | |
| 471 doc->ctx = fz_clone_context(ctx); | |
| 472 doc->progress = progressFn; | |
| 473 doc->error = errorFn; | |
| 474 doc->cookie = cookie; | |
| 475 doc->path = fz_strdup(ctx, path); | |
| 476 if (mu_create_semaphore(&doc->password_sem)) | |
| 477 goto fail; | |
| 478 | |
| 479 if (mu_create_thread(&doc->thread, load_worker, doc)) | |
| 480 goto fail; | |
| 481 | |
| 482 *pDoc = doc; | |
| 483 | |
| 484 return MuError_OK; | |
| 485 fail: | |
| 486 mu_destroy_semaphore(&doc->password_sem); | |
| 487 Pal_Mem_free(doc); | |
| 488 | |
| 489 return MuError_OOM; | |
| 490 } | |
| 491 | |
| 492 /** | |
| 493 * Provide the password for a document | |
| 494 * | |
| 495 * This function should be called to provide a password with a document | |
| 496 * error of MuOfficeError_PasswordRequired is received. | |
| 497 * | |
| 498 * If a password is requested again, this means the password was incorrect. | |
| 499 * | |
| 500 * @param doc the document object | |
| 501 * @param password the password (UTF8 encoded) | |
| 502 * @return error indication - 0 for success | |
| 503 */ | |
| 504 int MuOfficeDoc_providePassword(MuOfficeDoc *doc, const char *password) | |
| 505 { | |
| 506 size_t len; | |
| 507 | |
| 508 if (doc->password) | |
| 509 return MuError_PasswordPending; | |
| 510 if (!password) | |
| 511 password = ""; | |
| 512 | |
| 513 len = strlen(password); | |
| 514 doc->password = Pal_Mem_malloc(len+1); | |
| 515 strcpy(doc->password, password); | |
| 516 mu_trigger_semaphore(&doc->password_sem); | |
| 517 | |
| 518 return MuError_OK; | |
| 519 } | |
| 520 | |
| 521 /** | |
| 522 * Return the type of an open document | |
| 523 * | |
| 524 * @param doc the document object | |
| 525 * | |
| 526 * @return the document type | |
| 527 */ | |
| 528 MuOfficeDocType MuOfficeDoc_docType(MuOfficeDoc *doc) | |
| 529 { | |
| 530 return /* FIXME */MuOfficeDocType_PDF; | |
| 531 } | |
| 532 | |
| 533 static void | |
| 534 ensure_doc_loaded(MuOfficeDoc *doc) | |
| 535 { | |
| 536 if (doc == NULL) | |
| 537 return; | |
| 538 | |
| 539 mu_destroy_thread(&doc->thread); | |
| 540 } | |
| 541 | |
| 542 /** | |
| 543 * Return the number of pages of a document | |
| 544 * | |
| 545 * This function waits for document loading to complete before returning | |
| 546 * the result. It may block the calling thread for a significant period of | |
| 547 * time. To avoid blocking, this call should be avoided in favour of using | |
| 548 * the MuOfficeLib_loadDocument callbacks to monitor loading. | |
| 549 * | |
| 550 * If background loading fails, the associated error will be returned | |
| 551 * from this call. | |
| 552 * | |
| 553 * @param doc the document | |
| 554 * @param pNumPages address for return of the number of pages | |
| 555 * | |
| 556 * @return error indication - 0 for success | |
| 557 */ | |
| 558 MuError MuOfficeDoc_getNumPages(MuOfficeDoc *doc, int *pNumPages) | |
| 559 { | |
| 560 fz_context *ctx; | |
| 561 MuError err = MuError_OK; | |
| 562 | |
| 563 if (doc == NULL) | |
| 564 { | |
| 565 *pNumPages = 0; | |
| 566 return MuError_BadNull; | |
| 567 } | |
| 568 | |
| 569 ensure_doc_loaded(doc); | |
| 570 | |
| 571 ctx = doc->ctx; | |
| 572 | |
| 573 fz_try(ctx) | |
| 574 { | |
| 575 *pNumPages = fz_count_pages(ctx, doc->doc); | |
| 576 } | |
| 577 fz_catch(ctx) | |
| 578 { | |
| 579 err = MuError_Generic; | |
| 580 } | |
| 581 | |
| 582 return err; | |
| 583 } | |
| 584 | |
| 585 /** | |
| 586 * Determine if the document has been modified | |
| 587 * | |
| 588 * @param doc the document | |
| 589 * | |
| 590 * @return modified flag | |
| 591 */ | |
| 592 int MuOfficeDoc_hasBeenModified(MuOfficeDoc *doc) | |
| 593 { | |
| 594 fz_context *ctx; | |
| 595 pdf_document *pdoc; | |
| 596 int modified = 0; | |
| 597 | |
| 598 if (doc == NULL) | |
| 599 return 0; | |
| 600 | |
| 601 ensure_doc_loaded(doc); | |
| 602 | |
| 603 ctx = doc->ctx; | |
| 604 pdoc = pdf_specifics(ctx, doc->doc); | |
| 605 | |
| 606 if (pdoc == NULL) | |
| 607 return 0; | |
| 608 | |
| 609 fz_try(ctx) | |
| 610 modified = pdf_has_unsaved_changes(ctx, pdoc); | |
| 611 fz_catch(ctx) | |
| 612 modified = 0; | |
| 613 | |
| 614 return modified; | |
| 615 } | |
| 616 | |
| 617 /** | |
| 618 * Start a save operation | |
| 619 * | |
| 620 * @param doc the document | |
| 621 * @param path path of the file to which to save | |
| 622 * @param resultFn callback used to report completion | |
| 623 * @param cookie a pointer to pass to the callback | |
| 624 * | |
| 625 * @return error indication - 0 for success | |
| 626 */ | |
| 627 MuError MuOfficeDoc_save( MuOfficeDoc *doc, | |
| 628 const char *path, | |
| 629 MuOfficeSaveResultFn *resultFn, | |
| 630 void *cookie) | |
| 631 { | |
| 632 return MuError_NotImplemented; /* FIXME */ | |
| 633 } | |
| 634 | |
| 635 /** | |
| 636 * Stop a document loading. The document is not destroyed, but | |
| 637 * no further content will be read from the file. | |
| 638 * | |
| 639 * @param doc the MuOfficeDoc object | |
| 640 */ | |
| 641 void MuOfficeDoc_abortLoad(MuOfficeDoc *doc) | |
| 642 { | |
| 643 fz_context *ctx; | |
| 644 | |
| 645 if (doc == NULL) | |
| 646 return; | |
| 647 | |
| 648 ctx = doc->ctx; | |
| 649 doc->aborted = 1; | |
| 650 mu_trigger_semaphore(&doc->password_sem); | |
| 651 } | |
| 652 | |
| 653 /** | |
| 654 * Destroy a MuOfficeDoc object. Loading of the document is shutdown | |
| 655 * and no further callbacks will be issued for the specified object. | |
| 656 * | |
| 657 * @param doc the MuOfficeDoc object | |
| 658 */ | |
| 659 void MuOfficeDoc_destroy(MuOfficeDoc *doc) | |
| 660 { | |
| 661 MuOfficeDoc_abortLoad(doc); | |
| 662 mu_destroy_thread(&doc->thread); | |
| 663 mu_destroy_semaphore(&doc->password_sem); | |
| 664 | |
| 665 fz_drop_document(doc->ctx, doc->doc); | |
| 666 fz_drop_context(doc->ctx); | |
| 667 Pal_Mem_free(doc->path); | |
| 668 Pal_Mem_free(doc); | |
| 669 } | |
| 670 | |
| 671 /** | |
| 672 * Get a page of a document | |
| 673 * | |
| 674 * @param doc the document object | |
| 675 * @param pageNumber the number of the page to load (lying in the | |
| 676 * range 0 to one less than the number of pages) | |
| 677 * @param updateFn Function to be called back when the page updates | |
| 678 * @param cookie Opaque value to pass for any updates | |
| 679 * @param pPage Address for return of the page object | |
| 680 * | |
| 681 * @return error indication - 0 for success | |
| 682 */ | |
| 683 MuError MuOfficeDoc_getPage( MuOfficeDoc *doc, | |
| 684 int pageNumber, | |
| 685 MuOfficePageUpdateFn *updateFn, | |
| 686 void *cookie, | |
| 687 MuOfficePage **pPage) | |
| 688 { | |
| 689 MuOfficePage *page; | |
| 690 MuError err = MuError_OK; | |
| 691 fz_context *ctx; | |
| 692 | |
| 693 if (!doc) | |
| 694 return MuError_BadNull; | |
| 695 if (!pPage) | |
| 696 return MuError_OK; | |
| 697 | |
| 698 *pPage = NULL; | |
| 699 | |
| 700 ensure_doc_loaded(doc); | |
| 701 ctx = doc->ctx; | |
| 702 | |
| 703 page = Pal_Mem_calloc(1, sizeof(*page)); | |
| 704 if (page == NULL) | |
| 705 return MuError_OOM; | |
| 706 | |
| 707 muoffice_doc_lock(doc->mu); | |
| 708 | |
| 709 fz_try(ctx) | |
| 710 { | |
| 711 page->doc = doc; | |
| 712 page->pageNum = pageNumber; | |
| 713 page->cookie = cookie; | |
| 714 page->updateFn = updateFn; | |
| 715 page->page = fz_load_page(doc->ctx, doc->doc, pageNumber); | |
| 716 page->next = doc->pages; | |
| 717 doc->pages = page; | |
| 718 *pPage = page; | |
| 719 } | |
| 720 fz_catch(ctx) | |
| 721 { | |
| 722 Pal_Mem_free(page); | |
| 723 err = MuError_Generic; | |
| 724 } | |
| 725 | |
| 726 muoffice_doc_unlock(doc->mu); | |
| 727 | |
| 728 return err; | |
| 729 } | |
| 730 | |
| 731 /** | |
| 732 * Perform MuPDF native operations on a given document. | |
| 733 * | |
| 734 * The function is called with fz_context and fz_document | |
| 735 * values that can be safely used (i.e. the context is | |
| 736 * cloned/dropped appropriately around the function, and | |
| 737 * locking is used to ensure that no other threads are | |
| 738 * simultaneously using the document). Functions can | |
| 739 * signal errors by fz_throw-ing. | |
| 740 * | |
| 741 * Due to the locking, it is best to ensure that as little | |
| 742 * time is taken here as possible (i.e. if you fetch some | |
| 743 * data and then spend a long time processing it, it is | |
| 744 * probably best to fetch the data using MuOfficeDoc_run | |
| 745 * and then process it outside). This avoids potentially | |
| 746 * blocking the UI. | |
| 747 * | |
| 748 * @param doc the document object. | |
| 749 * @param fn the function to call with fz_context/fz_document | |
| 750 * values. | |
| 751 * @param arg Opaque data pointer. | |
| 752 * | |
| 753 * @return error indication - 0 for success | |
| 754 */ | |
| 755 MuError MuOfficeDoc_run(MuOfficeDoc *doc, void (*fn)(fz_context *ctx, fz_document *doc, void *arg), void *arg) | |
| 756 { | |
| 757 fz_context *ctx; | |
| 758 MuError err = MuError_OK; | |
| 759 | |
| 760 if (doc == NULL) | |
| 761 return MuError_BadNull; | |
| 762 if (fn == NULL) | |
| 763 return err; | |
| 764 | |
| 765 ensure_doc_loaded(doc); | |
| 766 | |
| 767 ctx = fz_clone_context(doc->mu->ctx); | |
| 768 if (ctx == NULL) | |
| 769 return MuError_OOM; | |
| 770 | |
| 771 muoffice_doc_lock(doc->mu); | |
| 772 | |
| 773 fz_try(ctx) | |
| 774 fn(ctx, doc->doc, arg); | |
| 775 fz_catch(ctx) | |
| 776 err = MuError_Generic; | |
| 777 | |
| 778 muoffice_doc_unlock(doc->mu); | |
| 779 | |
| 780 fz_drop_context(ctx); | |
| 781 | |
| 782 return err; | |
| 783 } | |
| 784 | |
| 785 /** | |
| 786 * Destroy a page object | |
| 787 * | |
| 788 * Note this does not delete or remove the page from the document. | |
| 789 * It simply destroys the page object which is merely a reference | |
| 790 * to the page. | |
| 791 * | |
| 792 * @param page the page object | |
| 793 */ | |
| 794 void MuOfficePage_destroy(MuOfficePage *page) | |
| 795 { | |
| 796 MuOfficeDoc *doc; | |
| 797 MuOfficePage **ptr; | |
| 798 | |
| 799 if (!page) | |
| 800 return; | |
| 801 | |
| 802 /* Unlink page from doc */ | |
| 803 doc = page->doc; | |
| 804 ptr = &doc->pages; | |
| 805 while (*ptr && *ptr != page) | |
| 806 ptr = &(*ptr)->next; | |
| 807 assert(*ptr); | |
| 808 *ptr = page->next; | |
| 809 | |
| 810 fz_drop_page(doc->ctx, page->page); | |
| 811 fz_drop_display_list(doc->ctx, page->list); | |
| 812 fz_free(doc->ctx, page); | |
| 813 } | |
| 814 | |
| 815 /** | |
| 816 * Get the size of a page in pixels | |
| 817 * | |
| 818 * This returns the size of the page in pixels. Pages can be rendered | |
| 819 * with a zoom factor. The returned value is the size of bitmap | |
| 820 * appropriate for rendering with a zoom of 1.0 and corresponds to | |
| 821 * 90 dpi. The returned values are not necessarily whole numbers. | |
| 822 * | |
| 823 * @param page the page object | |
| 824 * @param pWidth address for return of the width | |
| 825 * @param pHeight address for return of the height | |
| 826 * | |
| 827 * @return error indication - 0 for success | |
| 828 */ | |
| 829 MuError MuOfficePage_getSize( MuOfficePage *page, | |
| 830 float *pWidth, | |
| 831 float *pHeight) | |
| 832 { | |
| 833 MuOfficeDoc *doc; | |
| 834 fz_rect rect; | |
| 835 | |
| 836 if (!page) | |
| 837 return MuError_BadNull; | |
| 838 doc = page->doc; | |
| 839 if (!doc) | |
| 840 return MuError_BadNull; | |
| 841 | |
| 842 rect = fz_bound_page(doc->ctx, page->page); | |
| 843 | |
| 844 /* MuPDF measures in points (72ths of an inch). This API wants | |
| 845 * 90ths of an inch, so adjust. */ | |
| 846 | |
| 847 if (pWidth) | |
| 848 *pWidth = 90 * (rect.x1 - rect.x0) / 72; | |
| 849 if (pHeight) | |
| 850 *pHeight = 90 * (rect.y1 - rect.y0) / 72; | |
| 851 | |
| 852 return MuError_OK; | |
| 853 } | |
| 854 | |
| 855 /** | |
| 856 * Return the zoom factors necessary to render at to a given | |
| 857 * size in pixels. (deprecated) | |
| 858 * | |
| 859 * @param page the page object | |
| 860 * @param width the desired width | |
| 861 * @param height the desired height | |
| 862 * @param pXZoom Address for return of zoom necessary to fit width | |
| 863 * @param pYZoom Address for return of zoom necessary to fit height | |
| 864 * | |
| 865 * @return error indication - 0 for success | |
| 866 */ | |
| 867 MuError MuOfficePage_calculateZoom( MuOfficePage *page, | |
| 868 int width, | |
| 869 int height, | |
| 870 float *pXZoom, | |
| 871 float *pYZoom) | |
| 872 { | |
| 873 MuOfficeDoc *doc; | |
| 874 fz_rect rect; | |
| 875 float w, h; | |
| 876 | |
| 877 if (!page) | |
| 878 return MuError_BadNull; | |
| 879 doc = page->doc; | |
| 880 if (!doc) | |
| 881 return MuError_BadNull; | |
| 882 | |
| 883 rect = fz_bound_page(doc->ctx, page->page); | |
| 884 | |
| 885 /* MuPDF measures in points (72ths of an inch). This API wants | |
| 886 * 90ths of an inch, so adjust. */ | |
| 887 w = 90 * (rect.x1 - rect.x0) / 72; | |
| 888 h = 90 * (rect.y1 - rect.y0) / 72; | |
| 889 | |
| 890 if (pXZoom) | |
| 891 *pXZoom = width/w; | |
| 892 if (pYZoom) | |
| 893 *pYZoom = height/h; | |
| 894 | |
| 895 return MuError_OK; | |
| 896 } | |
| 897 | |
| 898 /** | |
| 899 * Get the size of a page in pixels for a specified zoom factor | |
| 900 * (deprecated) | |
| 901 * | |
| 902 * This returns the size of bitmap that should be used to display | |
| 903 * the entire page at the given zoom factor. A zoom of 1.0 | |
| 904 * corresponds to 90 dpi. | |
| 905 * | |
| 906 * @param page the page object | |
| 907 * @param zoom the zoom factor | |
| 908 * @param pWidth address for return of the width | |
| 909 * @param pHeight address for return of the height | |
| 910 * | |
| 911 * @return error indication - 0 for success | |
| 912 */ | |
| 913 MuError MuOfficePage_getSizeForZoom( MuOfficePage *page, | |
| 914 float zoom, | |
| 915 int *pWidth, | |
| 916 int *pHeight) | |
| 917 { | |
| 918 MuOfficeDoc *doc; | |
| 919 fz_rect rect; | |
| 920 float w, h; | |
| 921 | |
| 922 if (!page) | |
| 923 return MuError_BadNull; | |
| 924 doc = page->doc; | |
| 925 if (!doc) | |
| 926 return MuError_BadNull; | |
| 927 | |
| 928 rect = fz_bound_page(doc->ctx, page->page); | |
| 929 | |
| 930 /* MuPDF measures in points (72ths of an inch). This API wants | |
| 931 * 90ths of an inch, so adjust. */ | |
| 932 w = 90 * (rect.x1 - rect.x0) / 72; | |
| 933 h = 90 * (rect.y1 - rect.y0) / 72; | |
| 934 | |
| 935 if (pWidth) | |
| 936 *pWidth = (int)(w * zoom + 0.5f); | |
| 937 if (pHeight) | |
| 938 *pHeight = (int)(h * zoom + 0.5f); | |
| 939 | |
| 940 return MuError_OK; | |
| 941 } | |
| 942 | |
| 943 /** | |
| 944 * Perform MuPDF native operations on a given page. | |
| 945 * | |
| 946 * The function is called with fz_context and fz_page | |
| 947 * values that can be safely used (i.e. the context is | |
| 948 * cloned/dropped appropriately around the function, and | |
| 949 * locking is used to ensure that no other threads are | |
| 950 * simultaneously using the document). Functions can | |
| 951 * signal errors by fz_throw-ing. | |
| 952 * | |
| 953 * Due to the locking, it is best to ensure that as little | |
| 954 * time is taken here as possible (i.e. if you fetch some | |
| 955 * data and then spend a long time processing it, it is | |
| 956 * probably best to fetch the data using MuOfficePage_run | |
| 957 * and then process it outside). This avoids potentially | |
| 958 * blocking the UI. | |
| 959 * | |
| 960 * @param page the page object. | |
| 961 * @param fn the function to call with fz_context/fz_document | |
| 962 * values. | |
| 963 * @param arg Opaque data pointer. | |
| 964 * | |
| 965 * @return error indication - 0 for success | |
| 966 */ | |
| 967 MuError MuOfficePage_run(MuOfficePage *page, void (*fn)(fz_context *ctx, fz_page *page, void *arg), void *arg) | |
| 968 { | |
| 969 fz_context *ctx; | |
| 970 MuError err = MuError_OK; | |
| 971 | |
| 972 if (page == NULL) | |
| 973 return MuError_BadNull; | |
| 974 if (fn == NULL) | |
| 975 return err; | |
| 976 | |
| 977 ctx = fz_clone_context(page->doc->mu->ctx); | |
| 978 if (ctx == NULL) | |
| 979 return MuError_OOM; | |
| 980 | |
| 981 muoffice_doc_lock(page->doc->mu); | |
| 982 | |
| 983 fz_try(ctx) | |
| 984 fn(ctx, page->page, arg); | |
| 985 fz_catch(ctx) | |
| 986 err = MuError_Generic; | |
| 987 | |
| 988 muoffice_doc_unlock(page->doc->mu); | |
| 989 | |
| 990 fz_drop_context(ctx); | |
| 991 | |
| 992 return err; | |
| 993 } | |
| 994 | |
| 995 static void render_worker(void *arg) | |
| 996 { | |
| 997 MuOfficeRender *render = (MuOfficeRender *)arg; | |
| 998 MuOfficePage *page = render->page; | |
| 999 fz_context *ctx = fz_clone_context(page->doc->ctx); | |
| 1000 int err = 0; | |
| 1001 fz_pixmap *pixmap = NULL; | |
| 1002 fz_device *dev = NULL; | |
| 1003 float scalex; | |
| 1004 float scaley; | |
| 1005 fz_rect page_bounds; | |
| 1006 int locked = 0; | |
| 1007 | |
| 1008 if (ctx == NULL) | |
| 1009 return; | |
| 1010 | |
| 1011 fz_var(pixmap); | |
| 1012 fz_var(dev); | |
| 1013 fz_var(locked); | |
| 1014 | |
| 1015 fz_try(ctx) | |
| 1016 { | |
| 1017 if (page->list == NULL) | |
| 1018 { | |
| 1019 muoffice_doc_lock(page->doc->mu); | |
| 1020 locked = 1; | |
| 1021 page->list = fz_new_display_list_from_page(ctx, page->page); | |
| 1022 locked = 0; | |
| 1023 muoffice_doc_unlock(page->doc->mu); | |
| 1024 } | |
| 1025 /* Make a pixmap from the bitmap */ | |
| 1026 if (!render->area_valid) | |
| 1027 { | |
| 1028 render->area.renderArea.x = 0; | |
| 1029 render->area.renderArea.y = 0; | |
| 1030 render->area.renderArea.width = render->bitmap->width; | |
| 1031 render->area.renderArea.height = render->bitmap->height; | |
| 1032 } | |
| 1033 pixmap = fz_new_pixmap_with_data(ctx, | |
| 1034 fz_device_rgb(ctx), | |
| 1035 render->area.renderArea.width, | |
| 1036 render->area.renderArea.height, | |
| 1037 NULL, | |
| 1038 1, | |
| 1039 render->bitmap->lineSkip, | |
| 1040 ((unsigned char *)render->bitmap->memptr) + | |
| 1041 render->bitmap->lineSkip * ((int)render->area.renderArea.x + (int)render->area.origin.x) + | |
| 1042 4 * ((int)render->area.renderArea.y + (int)render->area.origin.y)); | |
| 1043 /* Be a bit clever with the scaling to make sure we get | |
| 1044 * integer width/heights. First calculate the target | |
| 1045 * width/height. */ | |
| 1046 page_bounds = fz_bound_page(ctx, render->page->page); | |
| 1047 scalex = (int)(90 * render->zoom * (page_bounds.x1 - page_bounds.x0) / 72 + 0.5f); | |
| 1048 scaley = (int)(90 * render->zoom * (page_bounds.y1 - page_bounds.y0) / 72 + 0.5f); | |
| 1049 /* Now calculate the actual scale factors required */ | |
| 1050 scalex /= (page_bounds.x1 - page_bounds.x0); | |
| 1051 scaley /= (page_bounds.y1 - page_bounds.y0); | |
| 1052 /* Render the list */ | |
| 1053 fz_clear_pixmap_with_value(ctx, pixmap, 0xFF); | |
| 1054 dev = fz_new_draw_device(ctx, fz_post_scale(fz_translate(-page_bounds.x0, -page_bounds.y0), scalex, scaley), pixmap); | |
| 1055 fz_run_display_list(ctx, page->list, dev, fz_identity, fz_infinite_rect, NULL); | |
| 1056 fz_close_device(ctx, dev); | |
| 1057 } | |
| 1058 fz_always(ctx) | |
| 1059 { | |
| 1060 fz_drop_pixmap(ctx, pixmap); | |
| 1061 fz_drop_device(ctx, dev); | |
| 1062 } | |
| 1063 fz_catch(ctx) | |
| 1064 { | |
| 1065 if (locked) | |
| 1066 muoffice_doc_unlock(page->doc->mu); | |
| 1067 err = MuError_Generic; | |
| 1068 goto fail; | |
| 1069 } | |
| 1070 | |
| 1071 fail: | |
| 1072 if (render->progress) | |
| 1073 render->progress(render->cookie, err); | |
| 1074 render->error = err; | |
| 1075 | |
| 1076 fz_drop_context(ctx); | |
| 1077 } | |
| 1078 | |
| 1079 /** | |
| 1080 * Schedule the rendering of an area of document page to | |
| 1081 * an area of a bitmap. | |
| 1082 * | |
| 1083 * The alignment between page and bitmap is defined by specifying | |
| 1084 * document's origin within the bitmap, possibly either positive or | |
| 1085 * negative. A render object is returned via which the process can | |
| 1086 * be monitored or terminated. | |
| 1087 * | |
| 1088 * The progress function is called exactly once per render in either | |
| 1089 * the success or failure case. | |
| 1090 * | |
| 1091 * Note that, since a render object represents a running thread that | |
| 1092 * needs access to the page, document, and library objects, it is important | |
| 1093 * to call MuOfficeRender_destroy, not only before using or deallocating | |
| 1094 * the bitmap, but also before calling MuOfficePage_destroy, etc.. | |
| 1095 * | |
| 1096 * @param page the page to render | |
| 1097 * @param zoom the zoom factor | |
| 1098 * @param bitmap the bitmap | |
| 1099 * @param area area to render | |
| 1100 * @param progressFn the progress callback function | |
| 1101 * @param cookie a pointer to pass to the callback function | |
| 1102 * @param pRender Address for return of the render object | |
| 1103 * | |
| 1104 * @return error indication - 0 for success | |
| 1105 */ | |
| 1106 MuError MuOfficePage_render( MuOfficePage *page, | |
| 1107 float zoom, | |
| 1108 const MuOfficeBitmap *bitmap, | |
| 1109 const MuOfficeRenderArea *area, | |
| 1110 MuOfficeRenderProgressFn *progressFn, | |
| 1111 void *cookie, | |
| 1112 MuOfficeRender **pRender) | |
| 1113 { | |
| 1114 MuOfficeRender *render; | |
| 1115 MuOfficeDoc *doc; | |
| 1116 fz_context *ctx; | |
| 1117 | |
| 1118 if (!pRender) | |
| 1119 return MuError_BadNull; | |
| 1120 *pRender = NULL; | |
| 1121 if (!page) | |
| 1122 return MuError_BadNull; | |
| 1123 doc = page->doc; | |
| 1124 ctx = doc->ctx; | |
| 1125 | |
| 1126 render = Pal_Mem_calloc(1, sizeof(*render)); | |
| 1127 if (render == NULL) | |
| 1128 return MuError_OOM; | |
| 1129 | |
| 1130 render->page = page; | |
| 1131 render->zoom = zoom; | |
| 1132 render->bitmap = bitmap; | |
| 1133 if (area) | |
| 1134 { | |
| 1135 render->area = *area; | |
| 1136 render->area_valid = 1; | |
| 1137 } | |
| 1138 else | |
| 1139 { | |
| 1140 render->area_valid = 0; | |
| 1141 } | |
| 1142 render->progress = progressFn; | |
| 1143 render->cookie = cookie; | |
| 1144 | |
| 1145 if (mu_create_thread(&render->thread, render_worker, render)) | |
| 1146 { | |
| 1147 Pal_Mem_free(render); | |
| 1148 return MuError_OOM; | |
| 1149 } | |
| 1150 | |
| 1151 *pRender = render; | |
| 1152 | |
| 1153 return MuError_OK; | |
| 1154 } | |
| 1155 | |
| 1156 /** | |
| 1157 * Destroy a render | |
| 1158 * | |
| 1159 * This call destroys a MuOfficeRender object, aborting any current | |
| 1160 * render. | |
| 1161 * | |
| 1162 * This call is intended to support an app dealing with a user quickly | |
| 1163 * flicking through document pages. A render may be scheduled but, before | |
| 1164 * completion, be found not to be needed. In that case the bitmap will | |
| 1165 * need to be reused, which requires any existing render to be aborted. | |
| 1166 * The call to MuOfficeRender_destroy will cut short the render and | |
| 1167 * allow the bitmap to be reused immediately. | |
| 1168 * | |
| 1169 * @note If an active render thread is destroyed, it will be aborted. | |
| 1170 * While fast, this is not an instant operation. For maximum | |
| 1171 * responsiveness, it is best to 'abort' as soon as you realise you | |
| 1172 * don't need the render, and to destroy when you get the callback. | |
| 1173 * | |
| 1174 * @param render The render object | |
| 1175 */ | |
| 1176 void MuOfficeRender_destroy(MuOfficeRender *render) | |
| 1177 { | |
| 1178 if (!render) | |
| 1179 return; | |
| 1180 | |
| 1181 MuOfficeRender_abort(render); | |
| 1182 mu_destroy_thread(&render->thread); | |
| 1183 Pal_Mem_free(render); | |
| 1184 } | |
| 1185 | |
| 1186 /** | |
| 1187 * Abort a render | |
| 1188 * | |
| 1189 * This call aborts any rendering currently underway. The 'render | |
| 1190 * complete' callback (if any) given when the render was created will | |
| 1191 * still be called. If a render has completed, this call will have no | |
| 1192 * effect. | |
| 1193 * | |
| 1194 * This call will not block to wait for the render thread to stop, but | |
| 1195 * will cause it to stop as soon as it can in the background. | |
| 1196 * | |
| 1197 * @note It is important not to start any new render to the same bitmap | |
| 1198 * until the callback comes in (or until waitUntilComplete returns), as | |
| 1199 * otherwise you can have multiple renders drawing to the same bitmap | |
| 1200 * with unpredictable consequences. | |
| 1201 * | |
| 1202 * @param render The render object to abort | |
| 1203 */ | |
| 1204 void MuOfficeRender_abort(MuOfficeRender *render) | |
| 1205 { | |
| 1206 if (render) | |
| 1207 render->mu_cookie.abort = 1; | |
| 1208 } | |
| 1209 | |
| 1210 /** | |
| 1211 * Wait for a render to complete. | |
| 1212 * | |
| 1213 * This call will not return until rendering is complete, so on return | |
| 1214 * the bitmap will contain the page image (assuming the render didn't | |
| 1215 * run into an error condition) and will not be used further by any | |
| 1216 * background processing. Any error during rendering will be returned | |
| 1217 * from this function. | |
| 1218 * | |
| 1219 * This call may block the calling thread for a significant period of | |
| 1220 * time. To avoid blocking, supply a progress-monitoring callback | |
| 1221 * function to MuOfficePage_render. | |
| 1222 * | |
| 1223 * @param render The render object to destroy | |
| 1224 * @return render error condition - 0 for no error. | |
| 1225 */ | |
| 1226 MuError MuOfficeRender_waitUntilComplete(MuOfficeRender *render) | |
| 1227 { | |
| 1228 if (!render) | |
| 1229 return MuError_OK; | |
| 1230 | |
| 1231 mu_destroy_thread(&render->thread); | |
| 1232 | |
| 1233 return render->error; | |
| 1234 } |
