|
- #import <Foundation/Foundation.h>
- #import <Quartz/Quartz.h>
-
- #include <list>
- #include <algorithm>
- #include <stdio.h>
-
- struct FBDoc {
- NSString * fDocName;
- CGPDFDocumentRef fDocument;
- NSString * fLabel;
- size_t fStartPage;
- size_t fNumPages;
- bool fKeepWithPrev;
- bool fKeepWithNext;
-
- FBDoc(NSString * line);
- FBDoc(NSString * docName, CGPDFDocumentRef doc, NSString * line = nil);
- ~FBDoc();
- };
-
- FBDoc::FBDoc(NSString * line)
- : fDocName(nil), fDocument(nil), fLabel([line retain])
- {
- }
-
- FBDoc::FBDoc(NSString * name, CGPDFDocumentRef doc, NSString * line)
- : fDocName(name), fDocument(doc), fLabel(line),
- fStartPage(0), fKeepWithPrev(false), fKeepWithNext(false)
- {
- fNumPages = CGPDFDocumentGetNumberOfPages(doc);
- }
-
- typedef std::list<FBDoc *> FBDocList;
-
- size_t gNextPage = 1;
- FBDocList gIndexList;
- FILE * gIndex;
- CGContextRef gBookContext;
- CFURLRef gBookURL;
-
- void AddVerbatimLine(NSString * line)
- {
- gIndexList.push_back(new FBDoc(line));
- }
-
- void AddDocument(NSString * docPath, NSString * label, bool keepWithPrev)
- {
- CGPDFDocumentRef doc = nil;
-
- if (docPath) {
- NSURL * url = [NSURL fileURLWithPath:docPath];
-
- doc = CGPDFDocumentCreateWithURL((CFURLRef)url);
- if (!doc) {
- fprintf(stderr, "Can't read PDF document %s\n", [docPath UTF8String]);
-
- return;
- }
- }
-
- FBDoc * fbDoc = new FBDoc(docPath, doc, label);
-
- if (keepWithPrev)
- if (FBDoc * prev = gIndexList.back())
- prev->fKeepWithNext = fbDoc->fKeepWithPrev = true;
-
- gIndexList.push_back(fbDoc);
- }
-
- void SelectDocument(FBDoc * doc)
- {
- const float kFontSize = 20.0f;
- const float kXROff = -40.0f;
- const float kXLOff = 20.0f;
- const float kYOff = 20.0f;
-
- if (!gBookContext) {
- CGRect mediaBox = CGPDFDocumentGetMediaBox(doc->fDocument, 1);
- mediaBox.origin.x = mediaBox.origin.y = 0.0f;
- gBookContext = CGPDFContextCreateWithURL(gBookURL, &mediaBox, NULL);
- }
- doc->fStartPage = gNextPage;
- for (size_t i = 0; i++ < doc->fNumPages; ) {
- CGRect pageRect = CGPDFDocumentGetMediaBox(doc->fDocument, i);
- CGContextBeginPage(gBookContext, &pageRect);
- CGContextDrawPDFDocument(gBookContext, pageRect, doc->fDocument, i);
- if (gNextPage > 1) {
- CGContextSetTextMatrix(gBookContext, CGAffineTransformIdentity);
- CGContextSelectFont(gBookContext, "MarkerFelt-Thin", kFontSize,
- kCGEncodingMacRoman);
- char pageNum[5];
- sprintf(pageNum, "%d", gNextPage);
- const float xPos = pageRect.origin.x
- + (gNextPage&1) ? (pageRect.size.width+kXROff) : kXLOff;
- const float yPos = pageRect.origin.y+kYOff;
- CGContextShowTextAtPoint(gBookContext, xPos, yPos,
- pageNum, strlen(pageNum));
- }
- ++gNextPage;
- CGContextEndPage(gBookContext);
- }
- }
-
- void AssembleBook()
- {
- FBDocList::iterator i = gIndexList.begin();
-
- while (i != gIndexList.end()) {
- if ((*i)->fStartPage || !(*i)->fNumPages) {
- ++i; // Already mapped, skip
- } else if (gNextPage & 1) {
- //
- // Odd-numbered page, search for next unmapped single page song
- //
- for (FBDocList::iterator j=i; j != gIndexList.end(); ++j)
- if (!(*j)->fStartPage && (*j)->fNumPages == 1
- && !(*j)->fKeepWithNext && !(*j)->fKeepWithPrev
- ) {
- SelectDocument(*j);
-
- if (j==i)
- ++i;
-
- goto found_odd_song;
- }
- abort();
- found_odd_song:
- ;
- } else {
- //
- // Even-numbered page, search for multi-page songs or at least two
- // single page ones.
- //
- if ((*i)->fNumPages == 1) {
- FBDocList::iterator j = i;
- while (++j != gIndexList.end())
- if ((*j)->fStartPage || !(*j)->fNumPages) {
- continue; // Already used, try next one
- } else if ((*j)->fNumPages > 1 || (*j)->fKeepWithNext) {
- break; // Found multi-page, fail
- } else {
- //
- // Found two single page documents to combine
- //
- SelectDocument(*i);
- ++i;
- SelectDocument(*j);
- if (j==i)
- ++j;
- goto found_even_song;
- }
- if (j == gIndexList.end()) {
- //
- // At end, use single page
- //
- SelectDocument(*i);
- ++i;
-
- goto found_even_song;
- }
- }
- for (FBDocList::iterator j=i; j != gIndexList.end(); ++j)
- if (!(*j)->fStartPage
- && ((*j)->fNumPages > 1 || (*j)->fKeepWithNext)
- ) {
- SelectDocument(*j);
-
- if (j==i)
- ++i;
- while (++j != gIndexList.end() && (*j)->fKeepWithPrev)
- SelectDocument(*j);
-
- goto found_even_song;
- }
- abort();
- found_even_song:
- ;
- }
- }
- if (gBookContext)
- CGContextRelease(gBookContext);
- }
-
- void PrintIndexLine(FBDoc * line)
- {
- if (line->fLabel) {
- NSString * s;
-
- if (line->fDocument)
- s = [NSString stringWithFormat:line->fLabel, line->fStartPage];
- else
- s = line->fLabel;
-
- NSData * d = [s dataUsingEncoding:NSUTF8StringEncoding];
-
- fwrite([d bytes], 1, [d length], gIndex);
- fputc('\n', gIndex);
- }
- }
-
- void PrintIndex()
- {
- std::for_each(gIndexList.begin(), gIndexList.end(), PrintIndexLine);
- }
-
- void ProcessIndex(NSString * inIdx)
- {
- bool inPrePostLude = true;
- NSEnumerator * lines = [[inIdx componentsSeparatedByString:@"\n"]
- objectEnumerator];
- NSCharacterSet * nonBlank = [[NSCharacterSet whitespaceCharacterSet]
- invertedSet];
- while (NSString * line = [lines nextObject]) {
- NSScanner * lineScanner = [NSScanner scannerWithString:line];
- if ([lineScanner scanString:@"%%" intoString:nil]
- && [lineScanner isAtEnd]
- ) {
- //
- // Separate with %% lines
- //
- inPrePostLude = !inPrePostLude;
- } else if (inPrePostLude) {
- //
- // Verbatim copy to output
- //
- AddVerbatimLine(line);
- } else {
- //
- // In index proper
- //
- NSString * doc;
- NSString * label = nil;
- bool keepWithPrev = false;
- [lineScanner setScanLocation:0];
- if ([lineScanner scanString:@"%" intoString:nil]) {
- continue; // Comment line, skip
- } else if ([lineScanner scanString:@"-" intoString:nil]) {
- doc = nil; // No document
- } else {
- if ([lineScanner scanString:@"+" intoString:nil])
- keepWithPrev = true;
- if (![lineScanner scanCharactersFromSet:nonBlank intoString:&doc])
- continue; // Blank line, skip
- }
- label = [line substringFromIndex:[lineScanner scanLocation]];
- AddDocument(doc, label, keepWithPrev);
- }
- }
- AssembleBook();
- PrintIndex();
- }
-
- int main (int argc, const char * argv[])
- {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-
- if (!argv[1]) {
- fprintf(stderr, "Usage: FakeBooker <index.idx>\n");
- exit(1);
- }
- NSString * index = [NSString stringWithUTF8String:argv[1]];
- NSString * idxBase = [index stringByDeletingPathExtension];
- NSString * idx = [NSString stringWithContentsOfFile:index];
- if (!idx) {
- fprintf(stderr, "Index file `%s' can't be read.\n", argv[1]);
- exit(2);
- }
- NSString * toc = [idxBase stringByAppendingString:@"_index.pdf"];
- if ([[NSFileManager defaultManager] fileExistsAtPath:toc])
- AddDocument(toc, nil, false);
- gIndex = fopen([[[toc stringByDeletingPathExtension]
- stringByAppendingPathExtension:@"tex"]
- UTF8String], "w");
- gBookURL = (CFURLRef)[NSURL fileURLWithPath:
- [idxBase stringByAppendingPathExtension:@"pdf"]];
- ProcessIndex(idx);
-
- [pool release];
- return 0;
- }
|