forked from scribendi/cindex-mac
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathIRIndexPrintView.m
373 lines (345 loc) · 15.5 KB
/
IRIndexPrintView.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
//
// IRIndexPrintView.m
// Cindex
//
// Created by PL on 9/19/05.
// Copyright 2005 Indexing Research. All rights reserved.
//
#import "IRIndexDocument.h"
#import "IRIndexPrintView.h"
#import "IRIndexDocWController.h"
#import "type.h"
#import "commandutils.h"
#import "strings_c.h"
#import "AttributedStringCategories.h"
#import "IRAttributedDisplayString.h"
#import "LayoutDescriptor.h"
#import "StorageCategories.h"
#import "sort.h"
static unichar nl[] = {'\n','\n','\n','\n','\n','\n','\n','\n','\n','\n'};
#define MAXWIDOWS (sizeof (nl)/sizeof(unichar))
static short getdatetime(char *dstring, char dateform, char timeflag, time_t time); /* forms long or short date string */
@interface IRIndexPrintView () {
// IRIndexDocument * _document;
time_t _currenttime;
NSUInteger _currentpage;
INDEX * FF;
BOOL silentMode;
NSTextStorage * _printstorage;
NSLayoutManager * _layoutmanager;
// IRAttributedDisplayString * _as;
NSUInteger _currententrylength;
BOOL _brokenheading;
long _overflow;
RECORD * _currentrecord, *previousRecord;
// int _totalpages;
NSRect _pagerect;
NSArray * _paragraphs;
RECN _consumed;
BOOL pageDone;
NSInteger targetPage;
PRINTFORMAT xf;
}
@property (weak) IRIndexDocument * document;
- (NSAttributedString *)getTitleString:(BOOL)header position:(int)sourceindex;
- (void)_addColumnForLayout:(NSLayoutManager *)lm;
@end
@implementation IRIndexPrintView
- (id)initWithDocument:(IRIndexDocument *)document paragraphs:(NSArray *)paras {
self = [self initWithFrame:NSMakeRect(0,0,0,0)];
if (self) {
self.document = document;
FF = document.iIndex;
_paragraphs = paras;
NSPrintInfo * pinfo = [document printInfo];
_pagerect = NSMakeRect(0,0,[pinfo paperSize].width-[pinfo rightMargin]-[pinfo leftMargin],
[pinfo paperSize].height-[pinfo topMargin]-[pinfo bottomMargin]);
self.frame = _pagerect;
}
return self;
}
- (BOOL)buildPageStatistics {
silentMode = YES;
NSRange pr = NSMakeRange(0, ULONG_MAX);
[self knowsPageRange:&pr]; // force setup
for (int pcount = 1; FF->pf.totalpages < FF->pf.last && _currentrecord && _currentrecord->num != FF->pf.lastrec; pcount++) {
if (main_comiscancel())
return NO;
if (FF->pf.totalpages == FF->pf.first-1) { // if at start of target page
FF->pf.rnum = previousRecord ? previousRecord->num : _currentrecord->num; // starting record (page break detected after current record incremented)
FF->pf.pageout = 0; // reset page count
}
[self rectForPage:pcount];
if (FF->pf.last == INT_MAX) // if no restriction on page count
showprogress((100.*_consumed)/FF->head.rtot);
else
showprogress((100.*FF->pf.totalpages)/FF->pf.last);
FF->pf.pageout++;
}
showprogress(100.);
if (FF->pf.rnum) { // if produced anything in our range
NSUInteger firstCIndex = (FF->pf.first-1)*FF->head.formpars.pf.mc.ncols; // text container index for start of first page
NSUInteger lastCIndex = FF->pf.last*FF->head.formpars.pf.mc.ncols-1; // text container index for end of last page
if (lastCIndex >= _layoutmanager.textContainers.count) // if container for intended last page is beyond end of index
lastCIndex = _layoutmanager.textContainers.count-1; // set to last
NSRange grs = [_layoutmanager glyphRangeForTextContainer:[[_layoutmanager textContainers] objectAtIndex:firstCIndex]];
NSRange gre = [_layoutmanager glyphRangeForTextContainer:[[_layoutmanager textContainers] objectAtIndex:lastCIndex]];
NSRange lineRange;
NSUInteger numlines, gindex;
for (numlines = 0, gindex = grs.location; gindex < NSMaxRange(gre); numlines++){
[_layoutmanager lineFragmentRectForGlyphAtIndex:gindex effectiveRange:&lineRange];
gindex = NSMaxRange(lineRange);
}
FF->pf.lines = (int)numlines;
FF->pf.characters = (int)[_layoutmanager characterRangeForGlyphRange:NSMakeRange(grs.location, NSMaxRange(gre)-grs.location) actualGlyphRange:NULL].length;
}
return YES;
}
- (BOOL)knowsPageRange:(NSRange *)rangeptr {
if (xf.firstrec != FF->pf.firstrec || xf.lastrec != FF->pf.lastrec ) { // if changed the records we want to see
xf = FF->pf; // update from index
_currentrecord = rec_getrec(FF,FF->pf.firstrec);
_printstorage = [[NSTextStorage alloc] init];
_layoutmanager = [[NSLayoutManager alloc] init];
[_layoutmanager setDelegate:self];
[_layoutmanager setBackgroundLayoutEnabled:NO];
[_printstorage addLayoutManager:_layoutmanager];
for (NSInteger vcount = self.subviews.count; vcount >= 0; vcount--) // remove any subviews from previous preview use
[self.subviews.lastObject removeFromSuperview];
}
// NSLog(@"PR: %@", NSStringFromRange(*rangeptr));
return YES;
}
- (NSRect)rectForPage:(NSInteger)page {
// NSLog(@"Seeking Page: %ld", page);
if (FF->pf.totalpages < page && _currentrecord && _currentrecord->num != FF->pf.lastrec) { // not already generated, and there's still some to generate
targetPage = page;
pageDone = NO;
while (_currentrecord && !pageDone && _currentrecord->num != FF->pf.lastrec) { // while need to generate
IRAttributedDisplayString *as = [[IRAttributedDisplayString alloc] initWithIRIndex:_document paragraphs:_paragraphs record:_currentrecord->num];
_currententrylength = [[as string] length];
_brokenheading = FALSE;
_overflow = 0;
[_printstorage appendAttributedString:as];
NSUInteger slength = [[_printstorage string] length];
[_printstorage linesForRange:NSMakeRange(slength-_currententrylength,_currententrylength)]; // get lines; force glyph generation
if (_overflow) { // if run in to new container
unsigned long baseindex = slength-_currententrylength; // start of text to manipulate
unsigned long gbase = baseindex; // glyph generation base
int hlevel = [[as attribute:IRHeadingLevelKey atIndex:0 effectiveRange:nil] intValue];
BOOL pushed = FALSE;
if (_brokenheading) {
int widows = [_printstorage linesForRange:NSMakeRange(baseindex,_currententrylength-_overflow)];
if (widows) {
if (widows <= MAXWIDOWS && (!FF->head.formpars.ef.runlevel || widows == 1 )) { // if have broken entry with pushable lines
[_printstorage beginEditing];
[_printstorage replaceCharactersInRange:NSMakeRange(baseindex,0) withString:[NSString stringWithCharacters:nl length:widows]];
[_printstorage endEditing];
baseindex += widows;
pushed = TRUE;
}
else // set base to index beyond widows
baseindex = slength-_overflow;
}
}
if ((hlevel > 0 || !pushed && FF->head.formpars.ef.runlevel == 1 && _brokenheading) && FF->head.privpars.vmode == VM_FULL && (FF->head.formpars.pf.mc.pgcont == RH_COL || FF->head.formpars.pf.mc.pgcont == RH_PAGE
&& (([[_layoutmanager textContainers] count])%FF->head.formpars.pf.mc.ncols) == 1)) {
unichar cc = [[_printstorage string] characterAtIndex:baseindex-1]; // is newline if end of para
if (cc != '\n') // if we inserted continuation text within a para
[_printstorage replaceCharactersInRange:NSMakeRange(baseindex++,0) withString:@"\n"]; // insert para break
FF->continued = TRUE;
IRAttributedDisplayString *cs = [[IRAttributedDisplayString alloc] initWithIRIndex:_document paragraphs:_paragraphs record:_currentrecord->num];
[_printstorage insertAttributedString:cs atIndex:baseindex];
if (cc != '\n') { // if we inserted continuation text within a para
NSMutableDictionary * tdic = [NSMutableDictionary dictionaryWithDictionary:[_printstorage attributesAtIndex:baseindex effectiveRange:NULL]];
NSMutableParagraphStyle * pm = [[NSMutableParagraphStyle alloc] init];
[pm setParagraphStyle:[tdic objectForKey:NSParagraphStyleAttributeName]];
[pm setFirstLineHeadIndent:[pm headIndent]];
[tdic setObject:pm forKey:NSParagraphStyleAttributeName]; // modify para style for continuation
[_printstorage setAttributes:tdic range:NSMakeRange(baseindex+[cs length], 1)]; // restore attributes
}
}
[_printstorage linesForRange:NSMakeRange(gbase,[[_printstorage string] length]-gbase)]; // force glyph generation
}
ENTRYINFO * eip = [as entryInformation];
_consumed += eip->consumed;
if (FF->pf.rnum && FF->pf.totalpages >= FF->pf.first-1 && FF->pf.totalpages <= FF->pf.last) { // if started counting (valid record) and within page range
FF->pf.entries++;
FF->pf.prefs += eip->prefs;
FF->pf.crefs += eip->crefs;
FF->pf.characters += _currententrylength;
FF->pf.lastrnum = _currentrecord->num;
if (eip->ulevel <= 0)
FF->pf.uniquemain++;
}
previousRecord = _currentrecord;
_currentrecord = [_document skip:1 from:_currentrecord];
}
}
if (FF->pf.totalpages >= page || FF->pf.totalpages == page-1 && ([[_layoutmanager textContainers] count]%FF->head.formpars.pf.mc.ncols)) // if filled to or beyond target page or filled on fractional page
return NSMakeRect(_pagerect.origin.x,(page-1)*_pagerect.size.height,_pagerect.size.width,_pagerect.size.height);
else
return NSZeroRect;
}
- (void)layoutManager:(NSLayoutManager *)layoutManager didCompleteLayoutForTextContainer:(NSTextContainer *)textContainer atEnd:(BOOL)layoutFinishedFlag {
if (textContainer == nil) { // if need to add container(s)
[self _addColumnForLayout:layoutManager];
// NSLog(@"Container Added: %ld", [layoutManager textContainers].count-1);
}
else {
if (!_overflow || silentMode) // if not redoing for overflow, or silent for statistics [last fix 3/17/20]
FF->pf.totalpages = _layoutmanager.textContainers.count/FF->head.formpars.pf.mc.ncols; // total completed
if (!layoutFinishedFlag) { // can't fit all in this container
NSRange gr = [layoutManager glyphRangeForTextContainer:textContainer];
unsigned long laidout = [layoutManager characterIndexForGlyphAtIndex:NSMaxRange(gr)-1]+1;
NSUInteger slength = [[_printstorage string] length];
_overflow = slength - laidout;
if (_overflow && slength != laidout + _currententrylength) // if heading broken
_brokenheading = TRUE;
NSUInteger index = [[layoutManager textContainers] indexOfObject:textContainer];
if (index == targetPage*FF->head.formpars.pf.mc.ncols-1) { // if at end of page we want
pageDone = TRUE;
// NSLog(@"Page %d Done: [%ld]", (long)targetPage, [[layoutManager textContainers] indexOfObject:textContainer]);
}
}
}
// else
// NSLog(@"Need More Text: [%ld]", [[layoutManager textContainers] indexOfObject:textContainer]);
}
- (void)_addColumnForLayout:(NSLayoutManager *)lm {
int voffset = _pagerect.size.height*([[lm textContainers] count]/FF->head.formpars.pf.mc.ncols); // vertical offset (height of all pages above current)
NSSize csize = _pagerect.size; // base container is page rect
NSSize newsize = NSMakeSize(_pagerect.size.width,_pagerect.size.height+voffset);
int colindex = [[lm textContainers] count]%FF->head.formpars.pf.mc.ncols;
[self setFrameSize:newsize]; // enlarge if necessary
if (FF->head.formpars.pf.mc.ncols > 1)
csize.width = (_pagerect.size.width-FF->head.formpars.pf.mc.ncols*FF->head.formpars.pf.mc.gutter)/FF->head.formpars.pf.mc.ncols;
NSTextContainer * tc = [[NSTextContainer alloc] initWithContainerSize:csize];
[lm addTextContainer:tc];
if (!silentMode) {
float xpos = colindex ? colindex * (csize.width+FF->head.formpars.pf.mc.gutter) : 0;
NSTextView * tv = [[NSTextView alloc] initWithFrame:NSMakeRect(xpos,newsize.height-csize.height,csize.width, csize.height) textContainer:tc];
[self addSubview:tv];
}
}
- (void)drawPageBorderWithSize:(NSSize)borderSize {
NSPrintOperation * po = [NSPrintOperation currentOperation];
NSPrintInfo * pinfo = [po printInfo];
NSRect frame = [self frame];
float writexpos, writeypos;
NSAttributedString * as;
_currentpage = [po currentPage] + FF->head.formpars.pf.firstpage-1; // add offset for base page number
[self setFrame:NSMakeRect(0,0,borderSize.width,borderSize.height)];
[self lockFocus];
as = [self getTitleString:YES position:0];
writexpos = [pinfo leftMargin];
writeypos = borderSize.height-([pinfo topMargin]+[as size].height)/2;
[as drawAtPoint:NSMakePoint(writexpos, writeypos)];
as = [self getTitleString:YES position:1];
writexpos = (borderSize.width-[as size].width)/2;
[as drawAtPoint:NSMakePoint(writexpos, writeypos)];
as = [self getTitleString:YES position:2];
writexpos = borderSize.width-[as size].width-[pinfo rightMargin];
[as drawAtPoint:NSMakePoint(writexpos, writeypos)];
as = [self getTitleString:NO position:0];
writexpos = [pinfo leftMargin];
writeypos = ([pinfo bottomMargin]-[as size].height)/2;
[as drawAtPoint:NSMakePoint(writexpos, writeypos)];
as = [self getTitleString:NO position:1];
writexpos = (borderSize.width-[as size].width)/2;
[as drawAtPoint:NSMakePoint(writexpos, writeypos)];
as = [self getTitleString:NO position:2];
writexpos = borderSize.width-[as size].width-[pinfo rightMargin];
[as drawAtPoint:NSMakePoint(writexpos, writeypos)];
[self unlockFocus];
[self setFrame:frame];
}
- (void)beginDocument {
[super beginDocument];
_currenttime = time(NULL);
}
- (void)endDocument {
[super endDocument];
}
- (NSAttributedString *)getTitleString:(BOOL)header position:(int)sourceindex {
BOOL rightpage = _currentpage&1 || !FF->head.formpars.pf.mc.reflect;
char base[500];
int limit = 500;
HEADERFOOTER * hfp;
char * source;
char cc, *tpos, *dest;
char fontid;
enum {
PNUM_ARAB = 0,
PNUM_ROMANLOWER,
PNUM_ROMANUPPER
};
if (rightpage)
hfp = header ? &FF->head.formpars.pf.righthead : &FF->head.formpars.pf.rightfoot;
else
hfp = header ? &FF->head.formpars.pf.lefthead : &FF->head.formpars.pf.leftfoot;
if (sourceindex == 0)
source = hfp->left;
else if (sourceindex == 1)
source = hfp->center;
else
source = hfp->right;
dest = base;
if (fontid = type_findlocal(FF->head.fm,hfp->hffont,0)) { // if want other than default font
*dest++ = FONTCHR;
*dest++ = fontid+FX_FONT;
}
if (hfp->hfstyle.style) {
*dest++ = CODECHR;
*dest++ = hfp->hfstyle.style;
}
while (*source && dest-base < limit) {
switch (cc = *source++) {
case ESCCHR: /* escaped special char */
if (*source)
*dest++ = *source++;
continue;
case '#': /* page # */
if (!FF->head.formpars.pf.numformat) /* arabic numerals */
dest += sprintf(dest,"%lu", (unsigned long)_currentpage);
else
dest += str_roman(dest, (int)_currentpage, FF->head.formpars.pf.numformat == PNUM_ROMANUPPER);
break;
case '@': /* date */
dest += getdatetime(dest,FF->head.formpars.pf.dateformat,FF->head.formpars.pf.timeflag,_currenttime);
break;
case '%': /* index file name */
strcpy(dest,(char *)[[FF->owner displayName] UTF8String]);
dest += strlen(dest);
break;
default:
*dest++ = cc;
}
}
if (dest-base >= limit)
dest = base+limit;
*dest = '\0';
if (hfp->hfstyle.cap == FC_INITIAL) {
unichar cu;
tpos = str_skipcodes(base);
while (*tpos == '\"' || *tpos == '\'')
tpos++;
cu = u8_toU(tpos);
if (u_islower(cu))
u8_appendU(tpos,u_toupper(cu)); // conversion relies on fact that uc and lc are same size
}
else if (hfp->hfstyle.cap == FC_UPPER)
str_upr(base);
return [NSAttributedString asFromXString:base fontMap:FF->head.fm size:hfp->size termchar:0];
}
@end
/**********************************************************************/
static short getdatetime(char *dstring, char dateform, char timeflag, time_t time) /* forms long or short date string */
{
NSDateFormatter * df = [[NSDateFormatter alloc] init];
[df setFormatterBehavior:NSDateFormatterBehavior10_4];
[df setDateStyle:dateform]; // 1 is short, 3 is long
[df setTimeStyle: timeflag ? NSDateFormatterShortStyle : dateform];
strcpy(dstring,(char *)[[df stringFromDate:[NSDate dateWithTimeIntervalSince1970:time]] cStringUsingEncoding:NSUTF8StringEncoding]);
return (strlen(dstring));
}