282 satır
7.0 KiB
Plaintext
282 satır
7.0 KiB
Plaintext
#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;
|
|
}
|