summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--misc/fontsample/.gitignore2
-rw-r--r--misc/fontsample/Makefile44
-rw-r--r--misc/fontsample/README.md21
-rwxr-xr-xmisc/fontsample/fontsamplebin0 -> 24500 bytes
-rw-r--r--misc/fontsample/fontsample.mm270
5 files changed, 337 insertions, 0 deletions
diff --git a/misc/fontsample/.gitignore b/misc/fontsample/.gitignore
new file mode 100644
index 000000000..6142305dc
--- /dev/null
+++ b/misc/fontsample/.gitignore
@@ -0,0 +1,2 @@
+*.o
+*.d
diff --git a/misc/fontsample/Makefile b/misc/fontsample/Makefile
new file mode 100644
index 000000000..063d7262d
--- /dev/null
+++ b/misc/fontsample/Makefile
@@ -0,0 +1,44 @@
+sources = fontsample.mm
+
+CC = clang
+CXX = clang
+LD = clang
+
+XFLAGS := -Wall -g -fno-common
+CFLAGS += -std=c11
+CXXFLAGS += -std=c++11 -stdlib=libc++ -fno-rtti -fno-exceptions
+LDFLAGS += -lc++
+
+#flags release and debug targets (e.g. make DEBUG=1)
+ifeq ($(strip $(DEBUG)),1)
+ XFLAGS += -O0
+# else
+# XFLAGS += -Os -DNDEBUG
+endif
+
+libs := -lobjc
+frameworks := -framework Foundation \
+ -framework CoreText \
+ -framework CoreServices \
+ -framework CoreGraphics \
+ -framework ImageIO
+
+c_flags = $(CFLAGS) $(XFLAGS) -MMD -fobjc-arc
+cxx_flags = $(CXXFLAGS) $(XFLAGS) -MMD -fobjc-arc
+ld_flags = $(LDFLAGS) $(libs) $(frameworks)
+
+objects := $(sources:%.c=%.o)
+objects := $(objects:%.cc=%.o)
+objects := $(objects:%.mm=%.o)
+
+fontsample: $(objects)
+ $(LD) $(ld_flags) -o $@ $^
+
+%.o: %.mm
+ $(CXX) $(cxx_flags) -c $<
+
+clean:
+ rm -f *.o
+
+all: fontsample
+.PHONY: all clean
diff --git a/misc/fontsample/README.md b/misc/fontsample/README.md
new file mode 100644
index 000000000..357b93e6f
--- /dev/null
+++ b/misc/fontsample/README.md
@@ -0,0 +1,21 @@
+# fontsample
+
+A macOS-specific program for generating a PDF with text sample of a
+specific font file.
+
+```
+$ make
+$ ./fontsample -h
+usage: ./fontsample [options] <fontfile>
+
+options:
+ -h, -help Show usage and exit.
+ -z, -size <size> Font size to render. Defaults to 96.
+ -t, -text <text> Text line to render. Defaults to "Rags78 **A**".
+ -o <file> Write output to <file> instead of default filename.
+ Defaults to <fontfile>.pdf. If the provided filename
+ ends with ".png" a PNG is written instead of a PDF.
+
+<fontfile>
+ Any font file that macOS can read.
+```
diff --git a/misc/fontsample/fontsample b/misc/fontsample/fontsample
new file mode 100755
index 000000000..d405bf511
--- /dev/null
+++ b/misc/fontsample/fontsample
Binary files differ
diff --git a/misc/fontsample/fontsample.mm b/misc/fontsample/fontsample.mm
new file mode 100644
index 000000000..2534603cd
--- /dev/null
+++ b/misc/fontsample/fontsample.mm
@@ -0,0 +1,270 @@
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+#import <CoreText/CoreText.h>
+#import <ImageIO/ImageIO.h>
+
+
+static const char* prog = "?";
+
+static struct Options {
+ NSString* output = nil;
+ CGFloat size = 96;
+ NSString* text = @"Rags78 **A**";
+}options{};
+
+static const char usagetemplate[] = ""
+"usage: %s [options] <fontfile>\n"
+"\n"
+"options:\n"
+" -h, -help Show usage and exit.\n"
+" -z, -size <size> Font size to render. Defaults to %g.\n"
+" -t, -text <text> Text line to render. Defaults to \"%s\".\n"
+" -o <file> Write output to <file> instead of default filename.\n"
+" Defaults to <fontfile>.pdf. If the provided filename\n"
+" ends with \".png\" a PNG is written instead of a PDF.\n"
+"\n"
+"<fontfile>\n"
+" Any font file that macOS can read.\n"
+;
+
+void usage() {
+ printf(usagetemplate, prog, options.size, options.text.UTF8String);
+}
+
+
+// must call CFRelease on the returned pointer
+CTFontRef loadFont(NSString* filename, CGFloat size) {
+ CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filename, kCFURLPOSIXPathStyle, false);
+
+ CGDataProviderRef dataProvider = CGDataProviderCreateWithURL(url);
+ if (!dataProvider) {
+ fprintf(stderr, "%s: failed to read file %s\n", prog, filename.UTF8String);
+ exit(1);
+ }
+
+ CGFontRef cgf = CGFontCreateWithDataProvider(dataProvider);
+ if (!cgf) {
+ fprintf(stderr, "%s: failed to parse font %s\n", prog, filename.UTF8String);
+ exit(1);
+ }
+
+ CTFontRef ctf = CTFontCreateWithGraphicsFont(cgf, size, nil, nil);
+ if (!ctf) {
+ fprintf(stderr, "%s: CTFontCreateWithGraphicsFont failed\n", prog);
+ exit(1);
+ }
+
+ CFRelease(cgf);
+ CFRelease(dataProvider);
+ CFRelease(url);
+ return ctf;
+}
+
+
+CTLineRef createTextLine(CTFontRef font, NSString* text) {
+ NSDictionary* attr = @{ (NSString*)kCTFontAttributeName: (__bridge id)font };
+ return CTLineCreateWithAttributedString((CFAttributedStringRef)[[NSAttributedString alloc] initWithString:text attributes:attr]);
+}
+
+
+void draw(CGContextRef ctx,
+ CTLineRef textLine,
+ CGFloat width,
+ CGFloat height,
+ CGFloat descent)
+{
+ // white background
+ CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0);
+ CGContextFillRect(ctx, {{0,0},{width,height}});
+
+ // draw text
+ CGContextSetTextPosition(ctx, 0, descent);
+ CTLineDraw(textLine, ctx);
+}
+
+
+void makePDF(CTLineRef textLine,
+ CGFloat width,
+ CGFloat height,
+ CGFloat descent,
+ NSString* filename)
+{
+ CFMutableDataRef consumerData = CFDataCreateMutable(kCFAllocatorDefault, 0);
+ CGDataConsumerRef contextConsumer = CGDataConsumerCreateWithCFData(consumerData);
+ assert(contextConsumer);
+ const CGRect mediaBox{{0,0},{width,height}};
+ auto ctx = CGPDFContextCreate(contextConsumer, &mediaBox, nil);
+ assert(ctx);
+ CGPDFContextBeginPage(ctx, nil);
+
+ draw(ctx, textLine, width, height, descent);
+
+ // CGContextDrawPDFPage(ctx, page);
+ CGPDFContextEndPage(ctx);
+ CGPDFContextClose(ctx);
+ CGContextRelease(ctx);
+ [(__bridge NSData*)consumerData writeToFile:filename atomically:NO];
+}
+
+
+BOOL writePNG(CGImageRef image, NSString *filename) {
+ CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:filename];
+ CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL);
+ if (!destination) {
+ NSLog(@"Failed to create CGImageDestination for %@", filename);
+ return NO;
+ }
+
+ CGImageDestinationAddImage(destination, image, nil);
+
+ if (!CGImageDestinationFinalize(destination)) {
+ NSLog(@"Failed to write image to %@", filename);
+ CFRelease(destination);
+ return NO;
+ }
+
+ CFRelease(destination);
+ return YES;
+}
+
+
+void makePNG(CTLineRef textLine,
+ CGFloat width,
+ CGFloat height,
+ CGFloat descent,
+ NSString* filename)
+{
+ size_t widthz = (size_t)ceilf(width);
+ size_t heightz = (size_t)ceilf(height);
+
+ void* data = malloc(widthz * heightz * 4);
+
+ // Create the context and fill it with white background
+ CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
+ CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
+ CGContextRef ctx = CGBitmapContextCreate(data, widthz, heightz, 8, widthz*4, space, bitmapInfo);
+ CGColorSpaceRelease(space);
+
+ draw(ctx, textLine, (CGFloat)widthz, (CGFloat)heightz, descent);
+
+ CGImageRef imageRef = CGBitmapContextCreateImage(ctx);
+ writePNG(imageRef, filename);
+
+ free(data);
+ CGImageRelease(imageRef);
+ CGContextRelease(ctx);
+}
+
+
+void pdfmake(NSString* fontfile) {
+ NSString* text = @"Rags78 **A**";
+
+ NSString* outfile = options.output;
+ if (outfile == nil) {
+ // default to fontfile.pdf
+ outfile = [fontfile.stringByDeletingPathExtension stringByAppendingPathExtension:@"pdf"];
+ }
+
+ // Create an attributed string with string and font information
+ CTFontRef font = loadFont(fontfile, options.size);
+
+ CTLineRef textLine = createTextLine(font, text);
+ if (!textLine) {
+ fprintf(stderr, "%s: invalid sample text\n", prog);
+ exit(1);
+ }
+
+ // get font metrics
+ CGFloat ascent, descent, leading;
+ CGFloat width = CTLineGetTypographicBounds(textLine, &ascent, &descent, &leading);
+ CGFloat height = ascent + descent;
+
+ printf("write %s\n", outfile.UTF8String);
+ if ([outfile.pathExtension.lowercaseString isEqualToString:@"png"]) {
+ makePNG(textLine, width, height, descent, outfile);
+ } else {
+ makePDF(textLine, width, height, descent, outfile);
+ }
+
+ CFRelease(textLine);
+ CFRelease(font);
+}
+
+
+void badarg(const char* msg, const char* arg) {
+ fprintf(stderr, "%s: %s %s\n", prog, msg, arg);
+ usage();
+ exit(1);
+}
+
+
+const char* getargval(const char* arg, int argi, int argc, const char * argv[]) {
+ int i = argi + 1;
+ if (i == argc || strlen(argv[i]) == 0 || argv[i][0] == '-') {
+ fprintf(stderr, "%s: missing value for argument %s\n", prog, arg);
+ usage();
+ exit(1);
+ }
+ return argv[i];
+}
+
+
+NSMutableArray<NSString*>* parseargs(int argc, const char * argv[]) {
+ auto args = [NSMutableArray<NSString*> new];
+ if (argc == 0) {
+ fprintf(stderr, "invalid arguments\n");
+ exit(0);
+ }
+ prog = argv[0];
+ for (int i = 1; i < argc; i++) {
+ auto arg = argv[i];
+ if (strlen(arg) > 1 && arg[0] == '-') {
+ if (strcmp(arg, "-h") == 0 || strcmp(arg, "-help") == 0) {
+ usage();
+ exit(0);
+ }
+ if (strcmp(arg, "-o") == 0) {
+ auto val = getargval(arg, i++, argc, argv);
+ options.output = [NSString stringWithUTF8String:val];
+
+ } else if (strcmp(arg, "-z") == 0 || strcmp(arg, "-size") == 0) {
+ auto val = getargval(arg, i++, argc, argv);
+ auto f = atof(val);
+ if (isnan(f) || f < 1) {
+ badarg("invalid number", val);
+ }
+ options.size = (CGFloat)f;
+
+ } else if (strcmp(arg, "-t") == 0 || strcmp(arg, "-text") == 0) {
+ auto val = getargval(arg, i++, argc, argv);
+ options.text = [NSString stringWithUTF8String:val];
+
+ } else {
+ badarg("unknown flag", arg);
+ }
+ continue;
+ }
+ [args addObject:[NSString stringWithUTF8String:arg]];
+ }
+ return args;
+}
+
+
+int main(int argc, const char * argv[]) {
+ @autoreleasepool {
+ auto fontfiles = parseargs(argc, argv);
+
+ if (fontfiles.count < 1) {
+ fprintf(stderr, "%s: missing <fontfile>\n", prog);
+ usage();
+ return 1;
+ } else if (fontfiles.count > 1) {
+ fprintf(stderr, "%s: extraneous argument after <fontfile>\n", prog);
+ usage();
+ return 1;
+ }
+
+ pdfmake(fontfiles[0]);
+ }
+ return 0;
+}