From c445538b312a45435df87086eed2e19b68ed5bc1 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 9 Nov 2011 21:02:08 +0100 Subject: Add simple document interface and XPS support to iOS app. --- ios/MuPDF.xcodeproj/project.pbxproj | 7 ++ ios/document.c | 146 +++++++++++++++++++++++++++++ ios/document.h | 31 ++++++ ios/main.m | 182 ++++++++++++++---------------------- 4 files changed, 255 insertions(+), 111 deletions(-) create mode 100644 ios/document.c create mode 100644 ios/document.h (limited to 'ios') diff --git a/ios/MuPDF.xcodeproj/project.pbxproj b/ios/MuPDF.xcodeproj/project.pbxproj index fade0624..61542780 100644 --- a/ios/MuPDF.xcodeproj/project.pbxproj +++ b/ios/MuPDF.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 9644E9A0146ACEC000E5B70A /* document.c in Sources */ = {isa = PBXBuildFile; fileRef = 9644E99E146ACEC000E5B70A /* document.c */; }; 9683F619145F4F84000E1607 /* About.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9683F618145F4F84000E1607 /* About.pdf */; }; 968F2E9C14539C880085264E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2E9B14539C880085264E /* UIKit.framework */; }; 968F2E9E14539C880085264E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 968F2E9D14539C880085264E /* Foundation.framework */; }; @@ -36,6 +37,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 9644E99E146ACEC000E5B70A /* document.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = document.c; sourceTree = ""; }; + 9644E99F146ACEC000E5B70A /* document.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = document.h; sourceTree = ""; }; 9683F618145F4F84000E1607 /* About.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = About.pdf; sourceTree = ""; }; 968461E214642DB00012AE09 /* libLibraries.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libLibraries.a; sourceTree = BUILT_PRODUCTS_DIR; }; 968F2E8E14539BEB0085264E /* build_libs.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build_libs.sh; sourceTree = ""; }; @@ -89,6 +92,8 @@ 96BD2B35145AC485001CEBC3 /* Icon-72.png */, 968F2E8F14539BEB0085264E /* Info.plist */, 968F2E8E14539BEB0085264E /* build_libs.sh */, + 9644E99E146ACEC000E5B70A /* document.c */, + 9644E99F146ACEC000E5B70A /* document.h */, 968F2E9014539BEB0085264E /* main.m */, ); name = Sources; @@ -245,6 +250,7 @@ buildActionMask = 2147483647; files = ( 968F2EB014539CDA0085264E /* main.m in Sources */, + 9644E9A0146ACEC000E5B70A /* document.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -362,6 +368,7 @@ 968461EC14642DB00012AE09 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; 968F2EAD14539C880085264E /* Build configuration list for PBXNativeTarget "MuPDF" */ = { isa = XCConfigurationList; diff --git a/ios/document.c b/ios/document.c new file mode 100644 index 00000000..85f8e381 --- /dev/null +++ b/ios/document.c @@ -0,0 +1,146 @@ +#include "fitz/fitz.h" +#include "pdf/mupdf.h" +#include "xps/muxps.h" +#include "document.h" + +struct document * +open_document(char *filename) +{ + fz_error error; + + if (strstr(filename, ".pdf") || strstr(filename, ".PDF")) { + struct document *doc = fz_malloc(sizeof *doc); + memset(doc, 0, sizeof *doc); + doc->number = -1; + error = pdf_open_xref(&doc->pdf, filename, ""); + if (error) { + fz_free(doc); + fz_rethrow(error, "cannot open pdf document"); + return NULL; + } + error = pdf_load_page_tree(doc->pdf); + if (error) { + pdf_free_xref(doc->pdf); + fz_free(doc); + fz_rethrow(error, "cannot open pdf document"); + return NULL; + } + return doc; + } else if (strstr(filename, ".xps") || strstr(filename, ".XPS")) { + struct document *doc = fz_malloc(sizeof *doc); + memset(doc, 0, sizeof *doc); + doc->number = -1; + error = xps_open_file(&doc->xps, filename); + if (error) { + fz_free(doc); + fz_rethrow(error, "cannot open xps document"); + return NULL; + } + return doc; + } else { + fz_throw("unknown document format"); + return NULL; + } +} + +int +count_pages(struct document *doc) +{ + if (doc->pdf) + return pdf_count_pages(doc->pdf); + else if (doc->xps) + return xps_count_pages(doc->xps); + else + return 1; +} + +static void +load_page(struct document *doc, int number) +{ + fz_error error; + if (doc->number == number) + return; + doc->number = number; + if (doc->pdf) { + if (doc->pdf_page) { + pdf_age_store(doc->pdf->store, 1); + pdf_free_page(doc->pdf_page); + } + doc->pdf_page = NULL; +printf("load pdf page %d\n", number); + error = pdf_load_page(&doc->pdf_page, doc->pdf, number); + if (error) + fz_catch(error, "cannot load page %d", number); + } + if (doc->xps) { + if (doc->xps_page) + xps_free_page(doc->xps, doc->xps_page); + doc->xps_page = NULL; +printf("load xps page %d\n", number); + error = xps_load_page(&doc->xps_page, doc->xps, number); + if (error) + fz_catch(error, "cannot load page %d", number); + } +} + +void +measure_page(struct document *doc, int number, float *w, float *h) +{ + load_page(doc, number); + if (doc->pdf_page) { + pdf_page *page = doc->pdf_page; + fz_rect mediabox = fz_transform_rect(fz_rotate(page->rotate), page->mediabox); + *w = mediabox.x1 - mediabox.x0; + *h = mediabox.y1 - mediabox.y0; + } + else if (doc->xps_page) { + xps_page *page = doc->xps_page; + *w = page->width * 72.0f / 96.0f; + *h = page->height * 72.0f / 96.0f; + } + else { + *w = *h = 72; + } + fz_flush_warnings(); +} + +void +draw_page(struct document *doc, int number, fz_device *dev, fz_matrix ctm) +{ + load_page(doc, number); + if (doc->pdf_page) { + pdf_page *page = doc->pdf_page; + fz_matrix page_ctm = fz_concat(fz_rotate(-page->rotate), fz_scale(1, -1)); + fz_rect mediabox = fz_transform_rect(page_ctm, page->mediabox); + page_ctm = fz_concat(page_ctm, fz_translate(-mediabox.x0, -mediabox.y0)); + ctm = fz_concat(page_ctm, ctm); + pdf_run_page(doc->pdf, page, dev, ctm); + } else if (doc->xps_page) { + xps_page *page = doc->xps_page; + fz_matrix page_ctm = fz_scale(72.0f / 96.0f, 72.0f / 96.0f); + ctm = fz_concat(page_ctm, ctm); + doc->xps->dev = dev; + xps_parse_fixed_page(doc->xps, ctm, page); + doc->xps->dev = NULL; + } + fz_flush_warnings(); +} + +void +close_document(struct document *doc) +{ + if (doc->pdf) { + if (doc->pdf_page) + pdf_free_page(doc->pdf_page); + if (doc->pdf->store) + pdf_free_store(doc->pdf->store); + doc->pdf->store = NULL; + pdf_free_xref(doc->pdf); + } + if (doc->xps) { + if (doc->xps_page) + xps_free_page(doc->xps, doc->xps_page); + xps_free_context(doc->xps); + } + fz_flush_warnings(); +} diff --git a/ios/document.h b/ios/document.h new file mode 100644 index 00000000..a16de309 --- /dev/null +++ b/ios/document.h @@ -0,0 +1,31 @@ +#ifndef _DOCUMENT_H_ +#define _DOCUMENT_H_ + +#ifndef _FITZ_H_ +#error "fitz.h must be included before document.h" +#endif + +#ifndef _MUPDF_H_ +#error "mupdf.h must be included before document.h" +#endif + +#ifndef _MUXPS_H_ +#error "muxps.h must be included before document.h" +#endif + +struct document +{ + pdf_xref *pdf; + xps_context *xps; + int number; + pdf_page *pdf_page; + xps_page *xps_page; +}; + +struct document *open_document(char *filename); +int count_pages(struct document *doc); +void measure_page(struct document *doc, int number, float *w, float *h); +void draw_page(struct document *doc, int number, fz_device *dev, fz_matrix ctm); +void close_document(struct document *doc); + +#endif diff --git a/ios/main.m b/ios/main.m index 0ab2ae70..4b1c5f1c 100644 --- a/ios/main.m +++ b/ios/main.m @@ -10,6 +10,8 @@ #include "pdf/mupdf.h" #include "xps/muxps.h" +#include "document.h" + #define GAP 20 #define INDICATOR_Y -44-24 @@ -37,7 +39,7 @@ static float screenScale = 1; @interface MuPageView : UIScrollView { - pdf_xref *xref; + struct document *doc; int number; UIActivityIndicatorView *loadingView; UIImageView *imageView; @@ -46,7 +48,7 @@ static float screenScale = 1; float tileScale; BOOL cancel; } -- (id) initWithFrame: (CGRect)frame xref: (pdf_xref*)aXref page: (int)aNumber; +- (id) initWithFrame: (CGRect)frame document: (struct document*)aDoc page: (int)aNumber; - (void) displayImage: (UIImage*)image; - (void) resizeImage; - (void) loadPage; @@ -58,7 +60,7 @@ static float screenScale = 1; @interface MuDocumentController : UIViewController { - pdf_xref *xref; + struct document *doc; NSString *key; NSMutableSet *visiblePages; NSMutableSet *recycledPages; @@ -180,104 +182,67 @@ static CGSize fitPageToScreen(CGSize page, CGSize screen) return CGSizeMake(hscale, vscale); } -static UIImage *renderPage(pdf_xref *xref, int number, CGSize screen) +static UIImage *renderPage(struct document *doc, int number, CGSize screenSize) { - fz_error error; - CGSize pagesize; - fz_rect mediabox; + CGSize pageSize; fz_bbox bbox; fz_matrix ctm; fz_device *dev; fz_pixmap *pix; - pdf_page *page; CGSize scale; - error = pdf_load_page(&page, xref, number); - if (error) { - showAlert(@"Cannot load page"); - return nil; - } - - screen.width *= screenScale; - screen.height *= screenScale; + screenSize.width *= screenScale; + screenSize.height *= screenScale; - mediabox = fz_transform_rect(fz_rotate(page->rotate), page->mediabox); - pagesize = CGSizeMake(mediabox.x1 - mediabox.x0, mediabox.y1 - mediabox.y0); - scale = fitPageToScreen(pagesize, screen); - - ctm = fz_concat(fz_rotate(-page->rotate), fz_scale(scale.width, -scale.height)); - mediabox = fz_transform_rect(ctm, page->mediabox); - ctm = fz_concat(ctm, fz_translate(-mediabox.x0, -mediabox.y0)); - bbox = fz_round_rect(fz_transform_rect(ctm, page->mediabox)); + measure_page(doc, number, &pageSize.width, &pageSize.height); + scale = fitPageToScreen(pageSize, screenSize); + ctm = fz_scale(scale.width, scale.height); + bbox = (fz_bbox){0, 0, pageSize.width * scale.width, pageSize.height * scale.height}; pix = fz_new_pixmap_with_rect(fz_device_rgb, bbox); fz_clear_pixmap_with_color(pix, 255); dev = fz_new_draw_device(glyphcache, pix); - pdf_run_page(xref, page, dev, ctm); + draw_page(doc, number, dev, ctm); fz_free_device(dev); - pdf_free_page(page); - pdf_age_store(xref->store, 3); - fz_flush_warnings(); - return newImageWithPixmap(pix); } -static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect tile, float zoom) +static UIImage *renderTile(struct document *doc, int number, CGSize screenSize, CGRect tileRect, float zoom) { - fz_error error; - CGSize pagesize; - fz_rect mediabox; - fz_rect tilebox; + CGSize pageSize; + fz_rect rect; fz_bbox bbox; fz_matrix ctm; fz_device *dev; fz_pixmap *pix; - pdf_page *page; CGSize scale; - error = pdf_load_page(&page, xref, number); - if (error) { - showAlert(@"Cannot load page"); - return nil; - } + screenSize.width *= screenScale; + screenSize.height *= screenScale; + tileRect.origin.x *= screenScale; + tileRect.origin.y *= screenScale; + tileRect.size.width *= screenScale; + tileRect.size.height *= screenScale; - screen.width *= screenScale; - screen.height *= screenScale; - tile.origin.x *= screenScale; - tile.origin.y *= screenScale; - tile.size.width *= screenScale; - tile.size.height *= screenScale; - - mediabox = fz_transform_rect(fz_rotate(page->rotate), page->mediabox); - pagesize = CGSizeMake(mediabox.x1 - mediabox.x0, mediabox.y1 - mediabox.y0); - scale = fitPageToScreen(pagesize, screen); - scale.width *= zoom; - scale.height *= zoom; - - ctm = fz_concat(fz_rotate(-page->rotate), fz_scale(scale.width, -scale.height)); - mediabox = fz_transform_rect(ctm, page->mediabox); - ctm = fz_concat(ctm, fz_translate(-mediabox.x0, -mediabox.y0)); - bbox = fz_round_rect(fz_transform_rect(ctm, page->mediabox)); - - tilebox.x0 = tile.origin.x + bbox.x0; - tilebox.y0 = tile.origin.y + bbox.y0; - tilebox.x1 = tilebox.x0 + tile.size.width; - tilebox.y1 = tilebox.y0 + tile.size.height; - bbox = fz_round_rect(tilebox); + measure_page(doc, number, &pageSize.width, &pageSize.height); + scale = fitPageToScreen(pageSize, screenSize); + ctm = fz_scale(scale.width * zoom, scale.height * zoom); + + rect.x0 = tileRect.origin.x; + rect.y0 = tileRect.origin.y; + rect.x1 = tileRect.origin.x + tileRect.size.width; + rect.y1 = tileRect.origin.y + tileRect.size.height; + bbox = fz_round_rect(rect); pix = fz_new_pixmap_with_rect(fz_device_rgb, bbox); fz_clear_pixmap_with_color(pix, 255); dev = fz_new_draw_device(glyphcache, pix); - pdf_run_page(xref, page, dev, ctm); + draw_page(doc, number, dev, ctm); fz_free_device(dev); - pdf_free_page(page); - pdf_age_store(xref->store, 3); - fz_flush_warnings(); - return newImageWithPixmap(pix); } @@ -453,11 +418,11 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til @implementation MuPageView -- (id) initWithFrame: (CGRect)frame xref: (pdf_xref*)aXref page: (int)aNumber +- (id) initWithFrame: (CGRect)frame document: (struct document*)aDoc page: (int)aNumber { self = [super initWithFrame: frame]; if (self) { - xref = aXref; + doc = aDoc; number = aNumber; cancel = NO; @@ -527,12 +492,12 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til - (void) loadPage { - if (number < 0 || number >= pdf_count_pages(xref)) + if (number < 0 || number >= count_pages(doc)) return; dispatch_async(queue, ^{ if (!cancel) { printf("render page %d\n", number); - UIImage *image = renderPage(xref, number, self.bounds.size); + UIImage *image = renderPage(doc, number, self.bounds.size); dispatch_async(dispatch_get_main_queue(), ^{ [self displayImage: image]; [image release]; @@ -663,7 +628,7 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til } printf("render tile\n"); - UIImage *image = renderTile(xref, number, pageSize, viewFrame, scale); + UIImage *image = renderTile(doc, number, pageSize, viewFrame, scale); dispatch_async(dispatch_get_main_queue(), ^{ isValid = CGRectEqualToRect(frame, tileFrame) && scale == tileScale; @@ -737,34 +702,29 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til strcat(filename, "/Documents/"); strcat(filename, [nsfilename UTF8String]); - printf("open xref '%s'\n", filename); + printf("open document '%s'\n", filename); - error = pdf_open_xref(&xref, filename, password); - if (error) { - showAlert(@"Cannot open PDF file"); - [self release]; - return nil; - } - - error = pdf_load_page_tree(xref); - if (error) { + doc = open_document(filename); + if (!doc) { showAlert(@"Cannot open document"); [self release]; return nil; } - NSMutableArray *titles = [[NSMutableArray alloc] init]; - NSMutableArray *pages = [[NSMutableArray alloc] init]; - loadOutline(titles, pages, xref); - if ([titles count]) { - outline = [[MuOutlineController alloc] initWithTarget: self titles: titles pages: pages]; - [[self navigationItem] setRightBarButtonItem: - [[UIBarButtonItem alloc] - initWithBarButtonSystemItem: UIBarButtonSystemItemBookmarks - target:self action:@selector(onShowOutline:)]]; + if (doc->pdf) { + NSMutableArray *titles = [[NSMutableArray alloc] init]; + NSMutableArray *pages = [[NSMutableArray alloc] init]; + loadOutline(titles, pages, doc->pdf); + if ([titles count]) { + outline = [[MuOutlineController alloc] initWithTarget: self titles: titles pages: pages]; + [[self navigationItem] setRightBarButtonItem: + [[UIBarButtonItem alloc] + initWithBarButtonSystemItem: UIBarButtonSystemItemBookmarks + target:self action:@selector(onShowOutline:)]]; + } + [titles release]; + [pages release]; } - [titles release]; - [pages release]; return self; } @@ -774,7 +734,7 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til [[NSUserDefaults standardUserDefaults] setObject: key forKey: @"OpenDocumentKey"]; current = [[NSUserDefaults standardUserDefaults] integerForKey: key]; - if (current < 0 || current >= pdf_count_pages(xref)) + if (current < 0 || current >= count_pages(doc)) current = 0; UIView *view = [[UIView alloc] initWithFrame: CGRectZero]; @@ -806,7 +766,7 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til slider = [[UISlider alloc] initWithFrame: CGRectZero]; [slider setMinimumValue: 0]; - [slider setMaximumValue: pdf_count_pages(xref) - 1]; + [slider setMaximumValue: count_pages(doc) - 1]; [slider addTarget: self action: @selector(onSlide:) forControlEvents: UIControlEventValueChanged]; [view addSubview: canvas]; @@ -831,11 +791,11 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til - (void) dealloc { - if (xref) { - pdf_xref *self_xref = xref; // don't auto-retain self here! + if (doc) { + struct document *self_doc = doc; // don't auto-retain self here! dispatch_async(queue, ^{ - printf("close xref\n"); - pdf_free_xref(self_xref); + printf("close document\n"); + close_document(self_doc); }); } [outline release]; @@ -853,10 +813,10 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til [slider setValue: current]; - [indicator setText: [NSString stringWithFormat: @" %d of %d ", current+1, pdf_count_pages(xref)]]; + [indicator setText: [NSString stringWithFormat: @" %d of %d ", current+1, count_pages(doc)]]; [canvas setContentInset: UIEdgeInsetsZero]; - [canvas setContentSize: CGSizeMake(pdf_count_pages(xref) * width, height)]; + [canvas setContentSize: CGSizeMake(count_pages(doc) * width, height)]; [canvas setContentOffset: CGPointMake(current * width, 0)]; [wrapper setWidth: width - GAP - 24]; @@ -924,7 +884,7 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til { int number = [slider value]; if ([slider isTracking]) - [indicator setText: [NSString stringWithFormat: @" %d of %d ", number+1, pdf_count_pages(xref)]]; + [indicator setText: [NSString stringWithFormat: @" %d of %d ", number+1, count_pages(doc)]]; else [self gotoPage: number animated: NO]; } @@ -967,7 +927,7 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til [[NSUserDefaults standardUserDefaults] setInteger: current forKey: key]; - [indicator setText: [NSString stringWithFormat: @" %d of %d ", current+1, pdf_count_pages(xref)]]; + [indicator setText: [NSString stringWithFormat: @" %d of %d ", current+1, count_pages(doc)]]; [slider setValue: current]; // swap the page views in and out @@ -990,14 +950,14 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til - (void) createPageView: (int)number { - if (number < 0 || number >= pdf_count_pages(xref)) + if (number < 0 || number >= count_pages(doc)) return; int found = 0; for (MuPageView *view in [canvas subviews]) if ([view number] == number) found = 1; if (!found) { - MuPageView *view = [[MuPageView alloc] initWithFrame: CGRectMake(number * width, 0, width-GAP, height) xref: xref page: number]; + MuPageView *view = [[MuPageView alloc] initWithFrame: CGRectMake(number * width, 0, width-GAP, height) document: doc page: number]; [visiblePages addObject: view]; [canvas addSubview: view]; [view release]; @@ -1008,8 +968,8 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til { if (number < 0) number = 0; - if (number >= pdf_count_pages(xref)) - number = pdf_count_pages(xref) - 1; + if (number >= count_pages(doc)) + number = count_pages(doc) - 1; if (animated) { // setContentOffset:animated: does not use the normal animation // framework. It also doesn't play nice with the tap gesture @@ -1030,7 +990,7 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til [canvas setContentOffset: CGPointMake(number * width, 0)]; [slider setValue: number]; - [indicator setText: [NSString stringWithFormat: @" %d of %d ", number+1, pdf_count_pages(xref)]]; + [indicator setText: [NSString stringWithFormat: @" %d of %d ", number+1, count_pages(doc)]]; [UIView commitAnimations]; } else { @@ -1064,7 +1024,7 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til [[[self navigationController] toolbar] setNeedsLayout]; // force layout! // use max_width so we don't clamp the content offset too early during animation - [canvas setContentSize: CGSizeMake(pdf_count_pages(xref) * max_width, height)]; + [canvas setContentSize: CGSizeMake(count_pages(doc) * max_width, height)]; [canvas setContentOffset: CGPointMake(current * width, 0)]; for (MuPageView *view in visiblePages) { @@ -1083,7 +1043,7 @@ static UIImage *renderTile(pdf_xref *xref, int number, CGSize screen, CGRect til - (void) didRotateFromInterfaceOrientation: (UIInterfaceOrientation)o { - [canvas setContentSize: CGSizeMake(pdf_count_pages(xref) * width, height)]; + [canvas setContentSize: CGSizeMake(count_pages(doc) * width, height)]; [canvas setContentOffset: CGPointMake(current * width, 0)]; } -- cgit v1.2.3