From 1ac855bf6e9bf818ae8c6b6fddcd63de3f6d530d Mon Sep 17 00:00:00 2001 From: Matthias Neeracher Date: Fri, 3 Nov 2017 07:18:44 +0100 Subject: [PATCH] Import into git --- FakeBooker.1 | 79 ++++++++ FakeBooker.mm | 281 +++++++++++++++++++++++++++ FakeBooker.xcodeproj/project.pbxproj | 232 ++++++++++++++++++++++ FakeBooker_Prefix.pch | 7 + 4 files changed, 599 insertions(+) create mode 100644 FakeBooker.1 create mode 100644 FakeBooker.mm create mode 100644 FakeBooker.xcodeproj/project.pbxproj create mode 100644 FakeBooker_Prefix.pch diff --git a/FakeBooker.1 b/FakeBooker.1 new file mode 100644 index 0000000..9a8f25c --- /dev/null +++ b/FakeBooker.1 @@ -0,0 +1,79 @@ +.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. +.\"See Also: +.\"man mdoc.samples for a complete listing of options +.\"man mdoc for the short list of editing options +.\"/usr/share/misc/mdoc.template +.Dd 9/20/06 \" DATE +.Dt FakeBooker 1 \" Program name and manual section number +.Os Darwin +.Sh NAME \" Section Header - required - don't modify +.Nm FakeBooker, +.\" The following lines are read in generating the apropos(man -k) database. Use only key +.\" words here as the database is built based on the words here and in the .ND line. +.Nm Other_name_for_same_program(), +.Nm Yet another name for the same program. +.\" Use .Nm macro to designate other names for the documented program. +.Nd This line parsed for whatis database. +.Sh SYNOPSIS \" Section Header - required - don't modify +.Nm +.Op Fl abcd \" [-abcd] +.Op Fl a Ar path \" [-a path] +.Op Ar file \" [file] +.Op Ar \" [file ...] +.Ar arg0 \" Underlined argument - use .Ar anywhere to underline +arg2 ... \" Arguments +.Sh DESCRIPTION \" Section Header - required - don't modify +Use the .Nm macro to refer to your program throughout the man page like such: +.Nm +Underlining is accomplished with the .Ar macro like this: +.Ar underlined text . +.Pp \" Inserts a space +A list of items with descriptions: +.Bl -tag -width -indent \" Begins a tagged list +.It item a \" Each item preceded by .It macro +Description of item a +.It item b +Description of item b +.El \" Ends the list +.Pp +A list of flags and their descriptions: +.Bl -tag -width -indent \" Differs from above in tag removed +.It Fl a \"-a flag as a list item +Description of -a flag +.It Fl b +Description of -b flag +.El \" Ends the list +.Pp +.\" .Sh ENVIRONMENT \" May not be needed +.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 +.\" .It Ev ENV_VAR_1 +.\" Description of ENV_VAR_1 +.\" .It Ev ENV_VAR_2 +.\" Description of ENV_VAR_2 +.\" .El +.Sh FILES \" File used or created by the topic of the man page +.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact +.It Pa /usr/share/file_name +FILE_1 description +.It Pa /Users/joeuser/Library/really_long_file_name +FILE_2 description +.El \" Ends the list +.\" .Sh DIAGNOSTICS \" May not be needed +.\" .Bl -diag +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .It Diagnostic Tag +.\" Diagnostic informtion here. +.\" .El +.Sh SEE ALSO +.\" List links in ascending order by section, alphabetically within a section. +.\" Please do not reference files that do not exist without filing a bug report +.Xr a 1 , +.Xr b 1 , +.Xr c 1 , +.Xr a 2 , +.Xr b 2 , +.Xr a 3 , +.Xr b 3 +.\" .Sh BUGS \" Document known, unremedied bugs +.\" .Sh HISTORY \" Document history if command behaves in a unique manner \ No newline at end of file diff --git a/FakeBooker.mm b/FakeBooker.mm new file mode 100644 index 0000000..0db94eb --- /dev/null +++ b/FakeBooker.mm @@ -0,0 +1,281 @@ +#import +#import + +#include +#include +#include + +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 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 \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; +} diff --git a/FakeBooker.xcodeproj/project.pbxproj b/FakeBooker.xcodeproj/project.pbxproj new file mode 100644 index 0000000..5e94947 --- /dev/null +++ b/FakeBooker.xcodeproj/project.pbxproj @@ -0,0 +1,232 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 44; + objects = { + +/* Begin PBXBuildFile section */ + 8DD76F9A0486AA7600D96B5E /* FakeBooker.mm in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* FakeBooker.mm */; settings = {ATTRIBUTES = (); }; }; + 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; }; + 8DD76F9F0486AA7600D96B5E /* FakeBooker.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859EA3029092ED04C91782 /* FakeBooker.1 */; }; + 95C2FC690AC4A35B008CB1FA /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95C2FC680AC4A35B008CB1FA /* ApplicationServices.framework */; }; + 95C732200AC26690005FD7F5 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95C7321F0AC26690005FD7F5 /* Quartz.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + 8DD76F9F0486AA7600D96B5E /* FakeBooker.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 08FB7796FE84155DC02AAC07 /* FakeBooker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FakeBooker.mm; sourceTree = ""; }; + 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 32A70AAB03705E1F00C91783 /* FakeBooker_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FakeBooker_Prefix.pch; sourceTree = ""; }; + 8DD76FA10486AA7600D96B5E /* FakeBooker */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = FakeBooker; sourceTree = BUILT_PRODUCTS_DIR; }; + 95C2FC680AC4A35B008CB1FA /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = ""; }; + 95C7321F0AC26690005FD7F5 /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = /System/Library/Frameworks/Quartz.framework; sourceTree = ""; }; + C6859EA3029092ED04C91782 /* FakeBooker.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = FakeBooker.1; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DD76F9B0486AA7600D96B5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */, + 95C732200AC26690005FD7F5 /* Quartz.framework in Frameworks */, + 95C2FC690AC4A35B008CB1FA /* ApplicationServices.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* FakeBooker */ = { + isa = PBXGroup; + children = ( + 08FB7795FE84155DC02AAC07 /* Source */, + C6859EA2029092E104C91782 /* Documentation */, + 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = FakeBooker; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 32A70AAB03705E1F00C91783 /* FakeBooker_Prefix.pch */, + 08FB7796FE84155DC02AAC07 /* FakeBooker.mm */, + ); + name = Source; + sourceTree = ""; + }; + 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 95C2FC680AC4A35B008CB1FA /* ApplicationServices.framework */, + 95C7321F0AC26690005FD7F5 /* Quartz.framework */, + 08FB779EFE84155DC02AAC07 /* Foundation.framework */, + ); + name = "External Frameworks and Libraries"; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8DD76FA10486AA7600D96B5E /* FakeBooker */, + ); + name = Products; + sourceTree = ""; + }; + C6859EA2029092E104C91782 /* Documentation */ = { + isa = PBXGroup; + children = ( + C6859EA3029092ED04C91782 /* FakeBooker.1 */, + ); + name = Documentation; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DD76F960486AA7600D96B5E /* FakeBooker */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "FakeBooker" */; + buildPhases = ( + 8DD76F990486AA7600D96B5E /* Sources */, + 8DD76F9B0486AA7600D96B5E /* Frameworks */, + 8DD76F9E0486AA7600D96B5E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FakeBooker; + productInstallPath = "$(HOME)/bin"; + productName = FakeBooker; + productReference = 8DD76FA10486AA7600D96B5E /* FakeBooker */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + }; + buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "FakeBooker" */; + compatibilityVersion = "Xcode 3.0"; + developmentRegion = en; + hasScannedForEncodings = 1; + knownRegions = ( + en, + ); + mainGroup = 08FB7794FE84155DC02AAC07 /* FakeBooker */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DD76F960486AA7600D96B5E /* FakeBooker */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DD76F990486AA7600D96B5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DD76F9A0486AA7600D96B5E /* FakeBooker.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB927508733DD40010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = FakeBooker_Prefix.pch; + INSTALL_PATH = "$(HOME)/bin"; + PRODUCT_NAME = FakeBooker; + SDKROOT = macosx; + ZERO_LINK = YES; + }; + name = Debug; + }; + 1DEB927608733DD40010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = FakeBooker_Prefix.pch; + INSTALL_PATH = "$(HOME)/bin"; + PRODUCT_NAME = FakeBooker; + SDKROOT = macosx; + }; + name = Release; + }; + 1DEB927908733DD40010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + }; + name = Debug; + }; + 1DEB927A08733DD40010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "FakeBooker" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB927508733DD40010E9CD /* Debug */, + 1DEB927608733DD40010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "FakeBooker" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB927908733DD40010E9CD /* Debug */, + 1DEB927A08733DD40010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/FakeBooker_Prefix.pch b/FakeBooker_Prefix.pch new file mode 100644 index 0000000..365cd4c --- /dev/null +++ b/FakeBooker_Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'FakeBooker' target in the 'FakeBooker' project. +// + +#ifdef __OBJC__ + #import +#endif