Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7643 views
1
#include "common.h"
2
#include "mupdf/pdf.h"
3
#import "MuWord.h"
4
#import "MuTextFieldController.h"
5
#import "MuAnnotation.h"
6
7
#import "MuPageViewNormal.h"
8
9
#define STRIKE_HEIGHT (0.375f)
10
#define UNDERLINE_HEIGHT (0.075f)
11
#define LINE_THICKNESS (0.07f)
12
#define INK_THICKNESS (4.0f)
13
14
static UIImage *newImageWithPixmap(fz_pixmap *pix, CGDataProviderRef cgdata)
15
{
16
CGImageRef cgimage = CreateCGImageWithPixmap(pix, cgdata);
17
UIImage *image = [[UIImage alloc] initWithCGImage: cgimage scale: screenScale orientation: UIImageOrientationUp];
18
CGImageRelease(cgimage);
19
return image;
20
}
21
22
static NSArray *enumerateWidgetRects(fz_document *doc, fz_page *page)
23
{
24
pdf_document *idoc = pdf_specifics(ctx, doc);
25
pdf_widget *widget;
26
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:10];
27
28
if (!idoc)
29
return nil;
30
31
for (widget = pdf_first_widget(ctx, idoc, (pdf_page *)page); widget; widget = pdf_next_widget(ctx, widget))
32
{
33
fz_rect rect;
34
35
pdf_bound_widget(ctx, widget, &rect);
36
[arr addObject:[NSValue valueWithCGRect:CGRectMake(
37
rect.x0,
38
rect.y0,
39
rect.x1-rect.x0,
40
rect.y1-rect.y0)]];
41
}
42
43
return [arr retain];
44
}
45
46
static NSArray *enumerateAnnotations(fz_document *doc, fz_page *page)
47
{
48
fz_annot *annot;
49
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:10];
50
51
for (annot = fz_first_annot(ctx, page); annot; annot = fz_next_annot(ctx, page, annot))
52
[arr addObject:[MuAnnotation annotFromAnnot:annot forPage:page]];
53
54
return [arr retain];
55
}
56
57
static NSArray *enumerateWords(fz_document *doc, fz_page *page)
58
{
59
fz_text_sheet *sheet = NULL;
60
fz_text_page *text = NULL;
61
fz_device *dev = NULL;
62
NSMutableArray *lns = [NSMutableArray array];
63
NSMutableArray *wds;
64
MuWord *word;
65
66
if (!lns)
67
return NULL;
68
69
fz_var(sheet);
70
fz_var(text);
71
fz_var(dev);
72
73
fz_try(ctx);
74
{
75
int b, l, c;
76
77
sheet = fz_new_text_sheet(ctx);
78
text = fz_new_text_page(ctx);
79
dev = fz_new_text_device(ctx, sheet, text);
80
fz_run_page(ctx, page, dev, &fz_identity, NULL);
81
fz_drop_device(ctx, dev);
82
dev = NULL;
83
84
for (b = 0; b < text->len; b++)
85
{
86
fz_text_block *block;
87
88
if (text->blocks[b].type != FZ_PAGE_BLOCK_TEXT)
89
continue;
90
91
block = text->blocks[b].u.text;
92
93
for (l = 0; l < block->len; l++)
94
{
95
fz_text_line *line = &block->lines[l];
96
fz_text_span *span;
97
98
wds = [NSMutableArray array];
99
if (!wds)
100
fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to create word array");
101
102
word = [MuWord word];
103
if (!word)
104
fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to create word");
105
106
for (span = line->first_span; span; span = span->next)
107
{
108
for (c = 0; c < span->len; c++)
109
{
110
fz_text_char *ch = &span->text[c];
111
fz_rect bbox;
112
CGRect rect;
113
114
fz_text_char_bbox(ctx, &bbox, span, c);
115
rect = CGRectMake(bbox.x0, bbox.y0, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0);
116
117
if (ch->c != ' ')
118
{
119
[word appendChar:ch->c withRect:rect];
120
}
121
else if (word.string.length > 0)
122
{
123
[wds addObject:word];
124
word = [MuWord word];
125
if (!word)
126
fz_throw(ctx, FZ_ERROR_GENERIC, "Failed to create word");
127
}
128
}
129
}
130
131
if (word.string.length > 0)
132
[wds addObject:word];
133
134
if ([wds count] > 0)
135
[lns addObject:wds];
136
}
137
}
138
}
139
fz_always(ctx);
140
{
141
fz_drop_text_page(ctx, text);
142
fz_drop_text_sheet(ctx, sheet);
143
fz_drop_device(ctx, dev);
144
}
145
fz_catch(ctx)
146
{
147
lns = NULL;
148
}
149
150
return [lns retain];
151
}
152
153
static void addMarkupAnnot(fz_document *doc, fz_page *page, int type, NSArray *rects)
154
{
155
pdf_document *idoc;
156
fz_point *quadpts = NULL;
157
float color[3];
158
float alpha;
159
float line_height;
160
float line_thickness;
161
162
idoc = pdf_specifics(ctx, doc);
163
if (!idoc)
164
return;
165
166
switch (type)
167
{
168
case FZ_ANNOT_HIGHLIGHT:
169
color[0] = 1.0;
170
color[1] = 1.0;
171
color[2] = 0.0;
172
alpha = 0.5;
173
line_thickness = 1.0;
174
line_height = 0.5;
175
break;
176
case FZ_ANNOT_UNDERLINE:
177
color[0] = 0.0;
178
color[1] = 0.0;
179
color[2] = 1.0;
180
alpha = 1.0;
181
line_thickness = LINE_THICKNESS;
182
line_height = UNDERLINE_HEIGHT;
183
break;
184
case FZ_ANNOT_STRIKEOUT:
185
color[0] = 1.0;
186
color[1] = 0.0;
187
color[2] = 0.0;
188
alpha = 1.0;
189
line_thickness = LINE_THICKNESS;
190
line_height = STRIKE_HEIGHT;
191
break;
192
193
default:
194
return;
195
}
196
197
fz_var(quadpts);
198
fz_try(ctx)
199
{
200
int i;
201
pdf_annot *annot;
202
203
quadpts = fz_malloc_array(ctx, (int)rects.count * 4, sizeof(fz_point));
204
for (i = 0; i < rects.count; i++)
205
{
206
CGRect rect = [[rects objectAtIndex:i] CGRectValue];
207
float top = rect.origin.y;
208
float bot = top + rect.size.height;
209
float left = rect.origin.x;
210
float right = left + rect.size.width;
211
quadpts[i*4].x = left;
212
quadpts[i*4].y = bot;
213
quadpts[i*4+1].x = right;
214
quadpts[i*4+1].y = bot;
215
quadpts[i*4+2].x = right;
216
quadpts[i*4+2].y = top;
217
quadpts[i*4+3].x = left;
218
quadpts[i*4+3].y = top;
219
}
220
221
annot = pdf_create_annot(ctx, idoc, (pdf_page *)page, type);
222
pdf_set_markup_annot_quadpoints(ctx, idoc, annot, quadpts, (int)rects.count*4);
223
pdf_set_markup_appearance(ctx, idoc, annot, color, alpha, line_thickness, line_height);
224
}
225
fz_always(ctx)
226
{
227
fz_free(ctx, quadpts);
228
}
229
fz_catch(ctx)
230
{
231
printf("Annotation creation failed\n");
232
}
233
}
234
235
static void addInkAnnot(fz_document *doc, fz_page *page, NSArray *curves)
236
{
237
pdf_document *idoc;
238
fz_point *pts = NULL;
239
int *counts = NULL;
240
int total;
241
float color[3] = {1.0, 0.0, 0.0};
242
243
idoc = pdf_specifics(ctx, doc);
244
if (!idoc)
245
return;
246
247
fz_var(pts);
248
fz_var(counts);
249
fz_try(ctx)
250
{
251
int i, j, k, n;
252
pdf_annot *annot;
253
254
n = (int)curves.count;
255
256
counts = fz_malloc_array(ctx, n, sizeof(int));
257
total = 0;
258
259
for (i = 0; i < n; i++)
260
{
261
NSArray *curve = [curves objectAtIndex:i];
262
counts[i] = (int)curve.count;
263
total += (int)curve.count;
264
}
265
266
pts = fz_malloc_array(ctx, total, sizeof(fz_point));
267
268
k = 0;
269
for (i = 0; i < n; i++)
270
{
271
NSArray *curve = [curves objectAtIndex:i];
272
int count = counts[i];
273
274
for (j = 0; j < count; j++)
275
{
276
CGPoint pt = [[curve objectAtIndex:j] CGPointValue];
277
pts[k].x = pt.x;
278
pts[k].y = pt.y;
279
k++;
280
}
281
}
282
283
annot = pdf_create_annot(ctx, idoc, (pdf_page *)page, FZ_ANNOT_INK);
284
pdf_set_ink_annot_list(ctx, idoc, annot, pts, counts, n, color, INK_THICKNESS);
285
}
286
fz_always(ctx)
287
{
288
fz_free(ctx, pts);
289
fz_free(ctx, counts);
290
}
291
fz_catch(ctx)
292
{
293
printf("Annotation creation failed\n");
294
}
295
}
296
297
static void deleteAnnotation(fz_document *doc, fz_page *page, int index)
298
{
299
pdf_document *idoc = pdf_specifics(ctx, doc);
300
if (!idoc)
301
return;
302
303
fz_try(ctx)
304
{
305
int i;
306
fz_annot *annot = fz_first_annot(ctx, page);
307
for (i = 0; i < index && annot; i++)
308
annot = fz_next_annot(ctx, page, annot);
309
310
if (annot)
311
pdf_delete_annot(ctx, idoc, (pdf_page *)page, (pdf_annot *)annot);
312
}
313
fz_catch(ctx)
314
{
315
printf("Annotation deletion failed\n");
316
}
317
}
318
319
static int setFocussedWidgetText(fz_document *doc, fz_page *page, const char *text)
320
{
321
int accepted = 0;
322
323
fz_var(accepted);
324
325
fz_try(ctx)
326
{
327
pdf_document *idoc = pdf_specifics(ctx, doc);
328
if (idoc)
329
{
330
pdf_widget *focus = pdf_focused_widget(ctx, idoc);
331
if (focus)
332
{
333
accepted = pdf_text_widget_set_text(ctx, idoc, focus, (char *)text);
334
}
335
}
336
}
337
fz_catch(ctx)
338
{
339
accepted = 0;
340
}
341
342
return accepted;
343
}
344
345
static int setFocussedWidgetChoice(fz_document *doc, fz_page *page, const char *text)
346
{
347
int accepted = 0;
348
349
fz_var(accepted);
350
351
fz_try(ctx)
352
{
353
pdf_document *idoc = pdf_specifics(ctx, doc);
354
if (idoc)
355
{
356
pdf_widget *focus = pdf_focused_widget(ctx, idoc);
357
if (focus)
358
{
359
pdf_choice_widget_set_value(ctx, idoc, focus, 1, (char **)&text);
360
accepted = 1;
361
}
362
}
363
}
364
fz_catch(ctx)
365
{
366
accepted = 0;
367
}
368
369
return accepted;
370
}
371
372
static fz_display_list *create_page_list(fz_document *doc, fz_page *page)
373
{
374
fz_display_list *list = NULL;
375
fz_device *dev = NULL;
376
377
fz_var(dev);
378
fz_try(ctx)
379
{
380
list = fz_new_display_list(ctx);
381
dev = fz_new_list_device(ctx, list);
382
fz_run_page_contents(ctx, page, dev, &fz_identity, NULL);
383
}
384
fz_always(ctx)
385
{
386
fz_drop_device(ctx, dev);
387
}
388
fz_catch(ctx)
389
{
390
return NULL;
391
}
392
393
return list;
394
}
395
396
static fz_display_list *create_annot_list(fz_document *doc, fz_page *page)
397
{
398
fz_display_list *list = NULL;
399
fz_device *dev = NULL;
400
401
fz_var(dev);
402
fz_try(ctx)
403
{
404
fz_annot *annot;
405
pdf_document *idoc = pdf_specifics(ctx, doc);
406
407
if (idoc)
408
pdf_update_page(ctx, idoc, (pdf_page *)page);
409
list = fz_new_display_list(ctx);
410
dev = fz_new_list_device(ctx, list);
411
for (annot = fz_first_annot(ctx, page); annot; annot = fz_next_annot(ctx, page, annot))
412
fz_run_annot(ctx, page, annot, dev, &fz_identity, NULL);
413
}
414
fz_always(ctx)
415
{
416
fz_drop_device(ctx, dev);
417
}
418
fz_catch(ctx)
419
{
420
return NULL;
421
}
422
423
return list;
424
}
425
426
static fz_pixmap *renderPixmap(fz_document *doc, fz_display_list *page_list, fz_display_list *annot_list, CGSize pageSize, CGSize screenSize, CGRect tileRect, float zoom)
427
{
428
fz_irect bbox;
429
fz_rect rect;
430
fz_matrix ctm;
431
fz_device *dev = NULL;
432
fz_pixmap *pix = NULL;
433
CGSize scale;
434
435
screenSize.width *= screenScale;
436
screenSize.height *= screenScale;
437
tileRect.origin.x *= screenScale;
438
tileRect.origin.y *= screenScale;
439
tileRect.size.width *= screenScale;
440
tileRect.size.height *= screenScale;
441
442
scale = fitPageToScreen(pageSize, screenSize);
443
fz_scale(&ctm, scale.width * zoom, scale.height * zoom);
444
445
bbox.x0 = tileRect.origin.x;
446
bbox.y0 = tileRect.origin.y;
447
bbox.x1 = tileRect.origin.x + tileRect.size.width;
448
bbox.y1 = tileRect.origin.y + tileRect.size.height;
449
fz_rect_from_irect(&rect, &bbox);
450
451
fz_var(dev);
452
fz_var(pix);
453
fz_try(ctx)
454
{
455
pix = fz_new_pixmap_with_bbox(ctx, fz_device_rgb(ctx), &bbox);
456
fz_clear_pixmap_with_value(ctx, pix, 255);
457
458
dev = fz_new_draw_device(ctx, pix);
459
fz_run_display_list(ctx, page_list, dev, &ctm, &rect, NULL);
460
fz_run_display_list(ctx, annot_list, dev, &ctm, &rect, NULL);
461
}
462
fz_always(ctx)
463
{
464
fz_drop_device(ctx, dev);
465
}
466
fz_catch(ctx)
467
{
468
fz_drop_pixmap(ctx, pix);
469
return NULL;
470
}
471
472
return pix;
473
}
474
475
typedef struct rect_list_s rect_list;
476
477
struct rect_list_s
478
{
479
fz_rect rect;
480
rect_list *next;
481
};
482
483
static void drop_list(rect_list *list)
484
{
485
while (list)
486
{
487
rect_list *n = list->next;
488
fz_free(ctx, list);
489
list = n;
490
}
491
}
492
493
static rect_list *updatePage(fz_document *doc, fz_page *page)
494
{
495
rect_list *list = NULL;
496
497
fz_var(list);
498
fz_try(ctx)
499
{
500
pdf_document *idoc = pdf_specifics(ctx, doc);
501
502
if (idoc)
503
{
504
fz_annot *annot;
505
506
pdf_update_page(ctx, idoc, (pdf_page *)page);
507
while ((annot = (fz_annot *)pdf_poll_changed_annot(ctx, idoc, (pdf_page *)page)) != NULL)
508
{
509
rect_list *node = fz_malloc_struct(ctx, rect_list);
510
511
fz_bound_annot(ctx, page, annot, &node->rect);
512
node->next = list;
513
list = node;
514
}
515
}
516
}
517
fz_catch(ctx)
518
{
519
drop_list(list);
520
list = NULL;
521
}
522
523
return list;
524
}
525
526
static void updatePixmap(fz_document *doc, fz_display_list *page_list, fz_display_list *annot_list, fz_pixmap *pixmap, rect_list *rlist, CGSize pageSize, CGSize screenSize, CGRect tileRect, float zoom)
527
{
528
fz_irect bbox;
529
fz_rect rect;
530
fz_matrix ctm;
531
fz_device *dev = NULL;
532
CGSize scale;
533
534
screenSize.width *= screenScale;
535
screenSize.height *= screenScale;
536
tileRect.origin.x *= screenScale;
537
tileRect.origin.y *= screenScale;
538
tileRect.size.width *= screenScale;
539
tileRect.size.height *= screenScale;
540
541
scale = fitPageToScreen(pageSize, screenSize);
542
fz_scale(&ctm, scale.width * zoom, scale.height * zoom);
543
544
bbox.x0 = tileRect.origin.x;
545
bbox.y0 = tileRect.origin.y;
546
bbox.x1 = tileRect.origin.x + tileRect.size.width;
547
bbox.y1 = tileRect.origin.y + tileRect.size.height;
548
fz_rect_from_irect(&rect, &bbox);
549
550
fz_var(dev);
551
fz_try(ctx)
552
{
553
while (rlist)
554
{
555
fz_irect abox;
556
fz_rect arect = rlist->rect;
557
fz_transform_rect(&arect, &ctm);
558
fz_intersect_rect(&arect, &rect);
559
fz_round_rect(&abox, &arect);
560
if (!fz_is_empty_irect(&abox))
561
{
562
fz_clear_pixmap_rect_with_value(ctx, pixmap, 255, &abox);
563
dev = fz_new_draw_device_with_bbox(ctx, pixmap, &abox);
564
fz_run_display_list(ctx, page_list, dev, &ctm, &arect, NULL);
565
fz_run_display_list(ctx, annot_list, dev, &ctm, &arect, NULL);
566
fz_drop_device(ctx, dev);
567
dev = NULL;
568
}
569
rlist = rlist->next;
570
}
571
}
572
fz_always(ctx)
573
{
574
fz_drop_device(ctx, dev);
575
}
576
fz_catch(ctx)
577
{
578
}
579
}
580
581
@implementation MuPageViewNormal
582
{
583
MuDocRef *docRef;
584
fz_document *doc;
585
fz_page *page;
586
fz_display_list *page_list;
587
fz_display_list *annot_list;
588
int number;
589
UIActivityIndicatorView *loadingView;
590
fz_pixmap *image_pix;
591
CGDataProviderRef imageData;
592
UIImageView *imageView;
593
fz_pixmap *tile_pix;
594
CGDataProviderRef tileData;
595
UIImageView *tileView;
596
MuHitView *hitView;
597
MuHitView *linkView;
598
MuTextSelectView *textSelectView;
599
MuInkView *inkView;
600
MuAnnotSelectView *annotSelectView;
601
NSArray *widgetRects;
602
NSArray *annotations;
603
int selectedAnnotationIndex;
604
CGSize pageSize;
605
CGRect tileFrame;
606
float tileScale;
607
BOOL cancel;
608
id<MuDialogCreator> dialogCreator;
609
id<MuUpdater> updater;
610
}
611
612
- (void) ensurePageLoaded
613
{
614
if (page)
615
return;
616
617
fz_try(ctx)
618
{
619
fz_rect bounds;
620
page = fz_load_page(ctx, doc, number);
621
fz_bound_page(ctx, page, &bounds);
622
pageSize.width = bounds.x1 - bounds.x0;
623
pageSize.height = bounds.y1 - bounds.y0;
624
}
625
fz_catch(ctx)
626
{
627
return;
628
}
629
}
630
631
- (void) ensureDisplaylists
632
{
633
[self ensurePageLoaded];
634
if (!page)
635
return;
636
637
if (!page_list)
638
page_list = create_page_list(doc, page);
639
640
if (!annot_list)
641
annot_list = create_annot_list(doc, page);
642
}
643
644
-(id) initWithFrame:(CGRect)frame dialogCreator:(id<MuDialogCreator>)dia updater:(id<MuUpdater>)upd document:(MuDocRef *)aDoc page:(int)aNumber
645
{
646
self = [super initWithFrame: frame];
647
if (self) {
648
docRef = [aDoc retain];
649
doc = docRef->doc;
650
number = aNumber;
651
cancel = NO;
652
dialogCreator = dia;
653
updater = upd;
654
selectedAnnotationIndex = -1;
655
656
[self setShowsVerticalScrollIndicator: NO];
657
[self setShowsHorizontalScrollIndicator: NO];
658
[self setDecelerationRate: UIScrollViewDecelerationRateFast];
659
[self setDelegate: self];
660
661
// zoomDidFinish/Begin events fire before bounce animation completes,
662
// making a mess when we rearrange views during the animation.
663
[self setBouncesZoom: NO];
664
665
[self resetZoomAnimated: NO];
666
667
// TODO: use a one shot timer to delay the display of this?
668
loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
669
[loadingView startAnimating];
670
[self addSubview: loadingView];
671
672
[self loadPage];
673
}
674
return self;
675
}
676
677
- (void) dealloc
678
{
679
// dealloc can trigger in background thread when the queued block is
680
// our last owner, and releases us on completion.
681
// Send the dealloc back to the main thread so we don't mess up UIKit.
682
if (![NSThread isMainThread]) {
683
__block id block_self = self; // don't auto-retain self!
684
dispatch_async(dispatch_get_main_queue(), ^{ [block_self dealloc]; });
685
} else {
686
__block fz_display_list *block_page_list = page_list;
687
__block fz_display_list *block_annot_list = annot_list;
688
__block fz_page *block_page = page;
689
// __block fz_document *block_doc = docRef->doc;
690
__block CGDataProviderRef block_tileData = tileData;
691
__block CGDataProviderRef block_imageData = imageData;
692
dispatch_async(queue, ^{
693
if (block_page_list)
694
fz_drop_display_list(ctx, block_page_list);
695
if (block_annot_list)
696
fz_drop_display_list(ctx, block_annot_list);
697
if (block_page)
698
fz_drop_page(ctx, block_page);
699
block_page = nil;
700
CGDataProviderRelease(block_tileData);
701
CGDataProviderRelease(block_imageData);
702
});
703
[docRef release];
704
[widgetRects release];
705
[linkView release];
706
[hitView release];
707
[textSelectView release];
708
[inkView release];
709
[annotSelectView release];
710
[tileView release];
711
[loadingView release];
712
[imageView release];
713
[super dealloc];
714
}
715
}
716
717
- (int) number
718
{
719
return number;
720
}
721
722
- (void) showLinks
723
{
724
if (!linkView) {
725
dispatch_async(queue, ^{
726
[self ensurePageLoaded];
727
fz_link *links = fz_load_links(ctx, page);
728
dispatch_async(dispatch_get_main_queue(), ^{
729
linkView = [[MuHitView alloc] initWithLinks: links forDocument: doc];
730
dispatch_async(queue, ^{
731
fz_drop_link(ctx, links);
732
});
733
if (imageView) {
734
[linkView setFrame: [imageView frame]];
735
[linkView setPageSize: pageSize];
736
}
737
[self addSubview: linkView];
738
});
739
});
740
}
741
}
742
743
- (void) hideLinks
744
{
745
[linkView removeFromSuperview];
746
[linkView release];
747
linkView = nil;
748
}
749
750
- (void) showSearchResults: (int)count
751
{
752
if (hitView) {
753
[hitView removeFromSuperview];
754
[hitView release];
755
hitView = nil;
756
}
757
hitView = [[MuHitView alloc] initWithSearchResults: count forDocument: doc];
758
if (imageView) {
759
[hitView setFrame: [imageView frame]];
760
[hitView setPageSize: pageSize];
761
}
762
[self addSubview: hitView];
763
}
764
765
- (void) clearSearchResults
766
{
767
if (hitView) {
768
[hitView removeFromSuperview];
769
[hitView release];
770
hitView = nil;
771
}
772
}
773
774
- (void) textSelectModeOn
775
{
776
dispatch_async(queue, ^{
777
[self ensurePageLoaded];
778
NSArray *words = enumerateWords(doc, page);
779
dispatch_sync(dispatch_get_main_queue(), ^{
780
textSelectView = [[MuTextSelectView alloc] initWithWords:words pageSize:pageSize];
781
[words release];
782
if (imageView)
783
[textSelectView setFrame:[imageView frame]];
784
[self addSubview:textSelectView];
785
});
786
});
787
}
788
789
- (void) inkModeOn
790
{
791
inkView = [[MuInkView alloc] initWithPageSize:pageSize];
792
if (imageView)
793
[inkView setFrame:[imageView frame]];
794
[self addSubview:inkView];
795
}
796
797
- (void) textSelectModeOff
798
{
799
[textSelectView removeFromSuperview];
800
[textSelectView release];
801
textSelectView = nil;
802
}
803
804
- (void) inkModeOff
805
{
806
[inkView removeFromSuperview];
807
[inkView release];
808
inkView = nil;
809
}
810
811
-(void) saveSelectionAsMarkup:(int)type
812
{
813
NSArray *rects = [textSelectView selectionRects];
814
if (rects.count == 0)
815
return;
816
817
[rects retain];
818
819
dispatch_async(queue, ^{
820
addMarkupAnnot(doc, page, type, rects);
821
[rects release];
822
dispatch_async(dispatch_get_main_queue(), ^{
823
[self update];
824
});
825
[self loadAnnotations];
826
});
827
[self textSelectModeOff];
828
}
829
830
-(void) saveInk
831
{
832
NSArray *curves = inkView.curves;
833
if (curves.count == 0)
834
return;
835
836
[curves retain];
837
838
dispatch_async(queue, ^{
839
addInkAnnot(doc, page, curves);
840
[curves release];
841
dispatch_async(dispatch_get_main_queue(), ^{
842
[self update];
843
});
844
[self loadAnnotations];
845
});
846
[self inkModeOff];
847
}
848
849
-(void) selectAnnotation:(int)i
850
{
851
selectedAnnotationIndex = i;
852
[annotSelectView removeFromSuperview];
853
[annotSelectView release];
854
annotSelectView = [[MuAnnotSelectView alloc] initWithAnnot:[annotations objectAtIndex:i] pageSize:pageSize];
855
[self addSubview:annotSelectView];
856
}
857
858
-(void) deselectAnnotation
859
{
860
selectedAnnotationIndex = -1;
861
[annotSelectView removeFromSuperview];
862
[annotSelectView release];
863
annotSelectView = nil;
864
}
865
866
-(void) deleteSelectedAnnotation
867
{
868
int index = selectedAnnotationIndex;
869
if (index >= 0)
870
{
871
dispatch_async(queue, ^{
872
deleteAnnotation(doc, page, index);
873
dispatch_async(dispatch_get_main_queue(), ^{
874
[self update];
875
});
876
[self loadAnnotations];
877
});
878
}
879
[self deselectAnnotation];
880
}
881
882
- (void) resetZoomAnimated: (BOOL)animated
883
{
884
// discard tile and any pending tile jobs
885
tileFrame = CGRectZero;
886
tileScale = 1;
887
if (tileView) {
888
[tileView removeFromSuperview];
889
[tileView release];
890
tileView = nil;
891
}
892
893
[self setMinimumZoomScale: 1];
894
[self setMaximumZoomScale: 5];
895
[self setZoomScale: 1 animated: animated];
896
}
897
898
- (void) removeFromSuperview
899
{
900
cancel = YES;
901
[super removeFromSuperview];
902
}
903
904
- (void) loadAnnotations
905
{
906
if (number < 0 || number >= fz_count_pages(ctx, doc))
907
return;
908
909
NSArray *annots = enumerateAnnotations(doc, page);
910
dispatch_async(dispatch_get_main_queue(), ^{
911
[annotations release];
912
annotations = annots;
913
});
914
}
915
916
- (void) loadPage
917
{
918
if (number < 0 || number >= fz_count_pages(ctx, doc))
919
return;
920
dispatch_async(queue, ^{
921
if (!cancel) {
922
printf("render page %d\n", number);
923
[self ensureDisplaylists];
924
CGSize scale = fitPageToScreen(pageSize, self.bounds.size);
925
CGRect rect = (CGRect){{0.0, 0.0},{pageSize.width * scale.width, pageSize.height * scale.height}};
926
image_pix = renderPixmap(doc, page_list, annot_list, pageSize, self.bounds.size, rect, 1.0);
927
CGDataProviderRelease(imageData);
928
imageData = CreateWrappedPixmap(image_pix);
929
UIImage *image = newImageWithPixmap(image_pix, imageData);
930
widgetRects = enumerateWidgetRects(doc, page);
931
[self loadAnnotations];
932
dispatch_async(dispatch_get_main_queue(), ^{
933
[self displayImage: image];
934
[image release];
935
});
936
} else {
937
printf("cancel page %d\n", number);
938
}
939
});
940
}
941
942
- (void) displayImage: (UIImage*)image
943
{
944
if (loadingView) {
945
[loadingView removeFromSuperview];
946
[loadingView release];
947
loadingView = nil;
948
}
949
950
if (hitView)
951
[hitView setPageSize: pageSize];
952
953
if (!imageView) {
954
imageView = [[UIImageView alloc] initWithImage: image];
955
imageView.opaque = YES;
956
[self addSubview: imageView];
957
if (hitView)
958
[self bringSubviewToFront: hitView];
959
if (textSelectView)
960
[self bringSubviewToFront:textSelectView];
961
if (inkView)
962
[self bringSubviewToFront:inkView];
963
if (annotSelectView)
964
[self bringSubviewToFront:annotSelectView];
965
} else {
966
[imageView setImage: image];
967
}
968
969
[self resizeImage];
970
}
971
972
- (void) resizeImage
973
{
974
if (imageView) {
975
CGSize imageSize = imageView.image.size;
976
CGSize scale = fitPageToScreen(imageSize, self.bounds.size);
977
if (fabs(scale.width - 1) > 0.1) {
978
CGRect frame = [imageView frame];
979
frame.size.width = imageSize.width * scale.width;
980
frame.size.height = imageSize.height * scale.height;
981
[imageView setFrame: frame];
982
983
printf("resized view; queuing up a reload (%d)\n", number);
984
dispatch_async(queue, ^{
985
dispatch_async(dispatch_get_main_queue(), ^{
986
CGSize scale = fitPageToScreen(imageView.image.size, self.bounds.size);
987
if (fabs(scale.width - 1) > 0.01)
988
[self loadPage];
989
});
990
});
991
} else {
992
[imageView sizeToFit];
993
}
994
995
[self setContentSize: imageView.frame.size];
996
997
[self layoutIfNeeded];
998
}
999
1000
}
1001
1002
- (void) willRotate
1003
{
1004
if (imageView) {
1005
[self resetZoomAnimated: NO];
1006
[self resizeImage];
1007
}
1008
}
1009
1010
- (void) layoutSubviews
1011
{
1012
[super layoutSubviews];
1013
1014
// center the image as it becomes smaller than the size of the screen
1015
1016
CGSize boundsSize = self.bounds.size;
1017
CGRect frameToCenter = loadingView ? loadingView.frame : imageView.frame;
1018
1019
// center horizontally
1020
if (frameToCenter.size.width < boundsSize.width)
1021
frameToCenter.origin.x = floor((boundsSize.width - frameToCenter.size.width) / 2);
1022
else
1023
frameToCenter.origin.x = 0;
1024
1025
// center vertically
1026
if (frameToCenter.size.height < boundsSize.height)
1027
frameToCenter.origin.y = floor((boundsSize.height - frameToCenter.size.height) / 2);
1028
else
1029
frameToCenter.origin.y = 0;
1030
1031
if (loadingView)
1032
loadingView.frame = frameToCenter;
1033
else
1034
imageView.frame = frameToCenter;
1035
1036
if (imageView)
1037
{
1038
CGRect frm = [imageView frame];
1039
1040
if (hitView)
1041
[hitView setFrame: frm];
1042
1043
if (linkView)
1044
[linkView setFrame:frm];
1045
1046
if (textSelectView)
1047
[textSelectView setFrame:frm];
1048
1049
if (inkView)
1050
[inkView setFrame:frm];
1051
1052
if (annotSelectView)
1053
[annotSelectView setFrame:frm];
1054
}
1055
}
1056
1057
- (UIView*) viewForZoomingInScrollView: (UIScrollView*)scrollView
1058
{
1059
return imageView;
1060
}
1061
1062
- (void) loadTile
1063
{
1064
CGSize screenSize = self.bounds.size;
1065
1066
tileFrame.origin = self.contentOffset;
1067
tileFrame.size = self.bounds.size;
1068
tileFrame = CGRectIntersection(tileFrame, imageView.frame);
1069
tileScale = self.zoomScale;
1070
1071
CGRect frame = tileFrame;
1072
float scale = tileScale;
1073
1074
CGRect viewFrame = frame;
1075
// Adjust viewFrame to be relative to imageView's origin
1076
viewFrame.origin.x -= imageView.frame.origin.x;
1077
viewFrame.origin.y -= imageView.frame.origin.y;
1078
1079
if (scale < 1.01)
1080
return;
1081
1082
dispatch_async(queue, ^{
1083
__block BOOL isValid;
1084
dispatch_sync(dispatch_get_main_queue(), ^{
1085
isValid = CGRectEqualToRect(frame, tileFrame) && scale == tileScale;
1086
});
1087
if (!isValid) {
1088
printf("cancel tile\n");
1089
return;
1090
}
1091
1092
[self ensureDisplaylists];
1093
1094
printf("render tile\n");
1095
tile_pix = renderPixmap(doc, page_list, annot_list, pageSize, screenSize, viewFrame, scale);
1096
CGDataProviderRelease(tileData);
1097
tileData = CreateWrappedPixmap(tile_pix);
1098
UIImage *image = newImageWithPixmap(tile_pix, tileData);
1099
1100
dispatch_async(dispatch_get_main_queue(), ^{
1101
isValid = CGRectEqualToRect(frame, tileFrame) && scale == tileScale;
1102
if (isValid) {
1103
if (tileView) {
1104
[tileView removeFromSuperview];
1105
[tileView release];
1106
tileView = nil;
1107
}
1108
1109
tileView = [[UIImageView alloc] initWithFrame: frame];
1110
[tileView setImage: image];
1111
[self addSubview: tileView];
1112
if (hitView)
1113
[self bringSubviewToFront: hitView];
1114
if (linkView)
1115
[self bringSubviewToFront:linkView];
1116
if (textSelectView)
1117
[self bringSubviewToFront:textSelectView];
1118
if (inkView)
1119
[self bringSubviewToFront:inkView];
1120
if (annotSelectView)
1121
[self bringSubviewToFront:annotSelectView];
1122
} else {
1123
printf("discard tile\n");
1124
}
1125
[image release];
1126
});
1127
});
1128
}
1129
1130
- (void) scrollViewDidScrollToTop:(UIScrollView *)scrollView { [self loadTile]; }
1131
- (void) scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { [self loadTile]; }
1132
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView { [self loadTile]; }
1133
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
1134
{
1135
if (!decelerate)
1136
[self loadTile];
1137
}
1138
1139
- (void) scrollViewWillBeginZooming: (UIScrollView*)scrollView withView: (UIView*)view
1140
{
1141
// discard tile and any pending tile jobs
1142
tileFrame = CGRectZero;
1143
tileScale = 1;
1144
if (tileView) {
1145
[tileView removeFromSuperview];
1146
[tileView release];
1147
tileView = nil;
1148
}
1149
}
1150
1151
- (void) scrollViewDidEndZooming: (UIScrollView*)scrollView withView: (UIView*)view atScale: (CGFloat)scale
1152
{
1153
[self loadTile];
1154
}
1155
1156
- (void) scrollViewDidZoom: (UIScrollView*)scrollView
1157
{
1158
if (imageView)
1159
{
1160
CGRect frm = [imageView frame];
1161
1162
if (hitView)
1163
[hitView setFrame: frm];
1164
1165
if (textSelectView)
1166
[textSelectView setFrame:frm];
1167
1168
if (inkView)
1169
[inkView setFrame:frm];
1170
1171
if (annotSelectView)
1172
[annotSelectView setFrame:frm];
1173
}
1174
}
1175
1176
- (void) setScale:(float)scale {}
1177
1178
- (void) updatePageAndTileWithTileFrame:(CGRect)tframe tileScale:(float)tscale viewFrame:(CGRect)vframe
1179
{
1180
rect_list *rlist = updatePage(doc, page);
1181
fz_drop_display_list(ctx, annot_list);
1182
annot_list = create_annot_list(doc, page);
1183
if (tile_pix)
1184
{
1185
updatePixmap(doc, page_list, annot_list, tile_pix, rlist, pageSize, self.bounds.size, vframe, tscale);
1186
UIImage *timage = newImageWithPixmap(tile_pix, tileData);
1187
dispatch_async(dispatch_get_main_queue(), ^{
1188
BOOL isValid = CGRectEqualToRect(tframe, tileFrame) && tscale == tileScale;
1189
if (isValid)
1190
[tileView setImage:timage];
1191
[timage release];
1192
});
1193
}
1194
CGSize fscale = fitPageToScreen(pageSize, self.bounds.size);
1195
CGRect rect = (CGRect){{0.0, 0.0},{pageSize.width * fscale.width, pageSize.height * fscale.height}};
1196
updatePixmap(doc, page_list, annot_list, image_pix, rlist, pageSize, self.bounds.size, rect, 1.0);
1197
drop_list(rlist);
1198
UIImage *image = newImageWithPixmap(image_pix, imageData);
1199
dispatch_async(dispatch_get_main_queue(), ^{
1200
[imageView setImage:image];
1201
[image release];
1202
});
1203
}
1204
1205
- (void) update
1206
{
1207
CGRect tframe = tileFrame;
1208
float tscale = tileScale;
1209
CGRect vframe = tframe;
1210
vframe.origin.x -= imageView.frame.origin.x;
1211
vframe.origin.y -= imageView.frame.origin.y;
1212
1213
dispatch_async(queue, ^{
1214
[self updatePageAndTileWithTileFrame:tframe tileScale:tscale viewFrame:vframe];
1215
});
1216
}
1217
1218
- (void) invokeTextDialog:(NSString *)text
1219
{
1220
[dialogCreator invokeTextDialog:text okayAction:^(NSString *newText) {
1221
dispatch_async(queue, ^{
1222
BOOL accepted = setFocussedWidgetText(doc, page, [newText UTF8String]);
1223
if (accepted)
1224
{
1225
dispatch_async(dispatch_get_main_queue(), ^{
1226
[updater update];
1227
});
1228
}
1229
else
1230
{
1231
dispatch_async(dispatch_get_main_queue(), ^{
1232
[self invokeTextDialog:newText];
1233
});
1234
}
1235
});
1236
}];
1237
}
1238
1239
- (void) invokeChoiceDialog:(NSArray *)choices
1240
{
1241
[dialogCreator invokeChoiceDialog:choices okayAction:^(NSArray *selection) {
1242
dispatch_async(queue, ^{
1243
BOOL accepted = setFocussedWidgetChoice(doc, page, [[selection objectAtIndex:0] UTF8String]);
1244
if (accepted)
1245
{
1246
dispatch_async(dispatch_get_main_queue(), ^{
1247
[updater update];
1248
});
1249
}
1250
else
1251
{
1252
dispatch_async(dispatch_get_main_queue(), ^{
1253
[self invokeChoiceDialog:choices];
1254
});
1255
}
1256
});
1257
1258
}];
1259
}
1260
1261
- (int) passTapToPage:(CGPoint)pt
1262
{
1263
pdf_document *idoc = pdf_specifics(ctx, doc);
1264
pdf_ui_event event;
1265
int changed = 0;
1266
pdf_widget *focus;
1267
char **opts = NULL;
1268
char *text = NULL;
1269
1270
if (!idoc)
1271
return 0;
1272
1273
fz_var(opts);
1274
fz_var(text);
1275
fz_try(ctx)
1276
{
1277
event.etype = PDF_EVENT_TYPE_POINTER;
1278
event.event.pointer.pt.x = pt.x;
1279
event.event.pointer.pt.y = pt.y;
1280
event.event.pointer.ptype = PDF_POINTER_DOWN;
1281
changed = pdf_pass_event(ctx, idoc, (pdf_page *)page, &event);
1282
event.event.pointer.ptype = PDF_POINTER_UP;
1283
changed |= pdf_pass_event(ctx, idoc, (pdf_page *)page, &event);
1284
1285
focus = pdf_focused_widget(ctx, idoc);
1286
if (focus)
1287
{
1288
switch (pdf_widget_get_type(ctx, focus))
1289
{
1290
case PDF_WIDGET_TYPE_TEXT:
1291
{
1292
text = pdf_text_widget_text(ctx, idoc, focus);
1293
NSString *stext = [[NSString stringWithUTF8String:text?text:""] retain];
1294
dispatch_async(dispatch_get_main_queue(), ^{
1295
[self invokeTextDialog:stext];
1296
[stext release];
1297
});
1298
break;
1299
}
1300
1301
case PDF_WIDGET_TYPE_LISTBOX:
1302
case PDF_WIDGET_TYPE_COMBOBOX:
1303
{
1304
int nopts = pdf_choice_widget_options(ctx, idoc, focus, NULL);
1305
opts = fz_malloc(ctx, nopts * sizeof(*opts));
1306
(void)pdf_choice_widget_options(ctx, idoc, focus, opts);
1307
NSMutableArray *arr = [[NSMutableArray arrayWithCapacity:nopts] retain];
1308
for (int i = 0; i < nopts; i++)
1309
{
1310
NSString *utf8 = [NSString stringWithUTF8String:opts[i]];
1311
// FIXME: temporary patch to handle the library not converting to utf8
1312
if (utf8 == nil)
1313
utf8 = [NSString stringWithCString:opts[i] encoding:NSASCIIStringEncoding];
1314
if (utf8 != nil)
1315
[arr addObject:utf8];
1316
}
1317
dispatch_async(dispatch_get_main_queue(), ^{
1318
[self invokeChoiceDialog:arr];
1319
[arr release];
1320
});
1321
break;
1322
}
1323
1324
case PDF_WIDGET_TYPE_SIGNATURE:
1325
break;
1326
1327
default:
1328
break;
1329
}
1330
}
1331
}
1332
fz_always(ctx)
1333
{
1334
fz_free(ctx, text);
1335
fz_free(ctx, opts);
1336
}
1337
fz_catch(ctx)
1338
{
1339
}
1340
1341
return changed;
1342
}
1343
1344
- (MuTapResult *) handleTap:(CGPoint)pt
1345
{
1346
CGPoint ipt = [self convertPoint:pt toView:imageView];
1347
CGSize scale = fitPageToScreen(pageSize, imageView.bounds.size);
1348
int i;
1349
1350
ipt.x /= scale.width;
1351
ipt.y /= scale.height;
1352
1353
for (i = 0; i < annotations.count; i++)
1354
{
1355
MuAnnotation *annot = [annotations objectAtIndex:i];
1356
if (annot.type != FZ_ANNOT_WIDGET && CGRectContainsPoint(annot.rect, ipt))
1357
{
1358
[self selectAnnotation:i];
1359
return [[[MuTapResultAnnotation alloc] initWithAnnotation:annot] autorelease];
1360
}
1361
}
1362
1363
[self deselectAnnotation];
1364
1365
for (i = 0; i < widgetRects.count; i++)
1366
{
1367
CGRect r = [[widgetRects objectAtIndex:i] CGRectValue];
1368
if (CGRectContainsPoint(r, ipt))
1369
{
1370
dispatch_async(queue, ^{
1371
int changed = [self passTapToPage:ipt];
1372
if (changed)
1373
dispatch_async(dispatch_get_main_queue(), ^{
1374
[updater update];
1375
});
1376
});
1377
return [[[MuTapResultWidget alloc] init] autorelease];
1378
}
1379
}
1380
1381
if (linkView)
1382
{
1383
CGPoint lpt = [self convertPoint:pt toView:linkView];
1384
return [linkView handleTap:lpt];
1385
}
1386
1387
return nil;
1388
}
1389
1390
@end
1391
1392