| 1 | """ |
|---|
| 2 | A module for automatically creating simple proof files from |
|---|
| 3 | WOFF files. *proofFont* is the only public function. |
|---|
| 4 | |
|---|
| 5 | This can also be used as a command line tool for generating |
|---|
| 6 | proofs from WOFF files. |
|---|
| 7 | """ |
|---|
| 8 | |
|---|
| 9 | # import test |
|---|
| 10 | |
|---|
| 11 | importErrors = [] |
|---|
| 12 | try: |
|---|
| 13 | import numpy |
|---|
| 14 | except: |
|---|
| 15 | importErrors.append("numpy") |
|---|
| 16 | try: |
|---|
| 17 | import fontTools |
|---|
| 18 | except ImportError: |
|---|
| 19 | importErrors.append("fontTools") |
|---|
| 20 | try: |
|---|
| 21 | import woffTools |
|---|
| 22 | except ImportError: |
|---|
| 23 | importErrors.append("woffTools") |
|---|
| 24 | |
|---|
| 25 | if importErrors: |
|---|
| 26 | import sys |
|---|
| 27 | print "Could not import needed module(s):", ", ".join(importErrors) |
|---|
| 28 | sys.exit() |
|---|
| 29 | |
|---|
| 30 | # import |
|---|
| 31 | |
|---|
| 32 | import os |
|---|
| 33 | import optparse |
|---|
| 34 | import unicodedata |
|---|
| 35 | from woffTools import WOFFFont |
|---|
| 36 | from woffTools.tools.css import makeFontFaceRule, makeFontFaceFontFamily |
|---|
| 37 | from woffTools.tools.support import startHTML, finishHTML, findUniqueFileName |
|---|
| 38 | |
|---|
| 39 | |
|---|
| 40 | # ---------------- |
|---|
| 41 | # Report Functions |
|---|
| 42 | # ---------------- |
|---|
| 43 | |
|---|
| 44 | def writeFileInfo(font, fontPath, writer): |
|---|
| 45 | # start the block |
|---|
| 46 | writer.begintag("div", c_l_a_s_s="infoBlock") |
|---|
| 47 | # title |
|---|
| 48 | writer.begintag("h3", c_l_a_s_s="infoBlockTitle") |
|---|
| 49 | writer.write("File Information") |
|---|
| 50 | writer.endtag("h3") |
|---|
| 51 | # table |
|---|
| 52 | writer.begintag("table", c_l_a_s_s="report") |
|---|
| 53 | writeFileInfoRow("FILE", os.path.basename(fontPath), writer) |
|---|
| 54 | writeFileInfoRow("DIRECTORY", os.path.dirname(fontPath), writer) |
|---|
| 55 | writeFileInfoRow("VERSION", "%d.%d" % (font.majorVersion, font.minorVersion), writer) |
|---|
| 56 | writer.endtag("table") |
|---|
| 57 | # close the container |
|---|
| 58 | writer.endtag("div") |
|---|
| 59 | |
|---|
| 60 | def writeFileInfoRow(title, value, writer): |
|---|
| 61 | # row |
|---|
| 62 | writer.begintag("tr") |
|---|
| 63 | # title |
|---|
| 64 | writer.begintag("td", c_l_a_s_s="title") |
|---|
| 65 | writer.write(title) |
|---|
| 66 | writer.endtag("td") |
|---|
| 67 | # message |
|---|
| 68 | writer.begintag("td") |
|---|
| 69 | writer.write(value) |
|---|
| 70 | writer.endtag("td") |
|---|
| 71 | # close row |
|---|
| 72 | writer.endtag("tr") |
|---|
| 73 | |
|---|
| 74 | def writeCharacterSet(font, writer, pointSizes=[9, 10, 11, 12, 14, 18, 24, 36, 48, 72], sampleText=None): |
|---|
| 75 | characterSet = makeCharacterSet(font) |
|---|
| 76 | for size in pointSizes: |
|---|
| 77 | # start the block |
|---|
| 78 | writer.begintag("div", c_l_a_s_s="infoBlock") |
|---|
| 79 | # title |
|---|
| 80 | writer.begintag("h4", c_l_a_s_s="infoBlockTitle") |
|---|
| 81 | writer.write("%dpx" % size) |
|---|
| 82 | writer.endtag("h4") |
|---|
| 83 | # character set |
|---|
| 84 | writer.begintag("p", style="font-size: %dpx;" % size, c_l_a_s_s="characterSet") |
|---|
| 85 | writer.write(characterSet) |
|---|
| 86 | writer.endtag("p") |
|---|
| 87 | # sample text |
|---|
| 88 | if sampleText: |
|---|
| 89 | writer.begintag("p", style="font-size: %dpx;" % size, c_l_a_s_s="sampleText") |
|---|
| 90 | writer.write(sampleText) |
|---|
| 91 | writer.endtag("p") |
|---|
| 92 | # close the container |
|---|
| 93 | writer.endtag("div") |
|---|
| 94 | |
|---|
| 95 | # ------------- |
|---|
| 96 | # Character Set |
|---|
| 97 | # ------------- |
|---|
| 98 | |
|---|
| 99 | def makeCharacterSet(font): |
|---|
| 100 | cmap = font["cmap"] |
|---|
| 101 | table = cmap.getcmap(3, 1) |
|---|
| 102 | mapping = table.cmap |
|---|
| 103 | categorizedCharacters = {} |
|---|
| 104 | glyphNameToCharacter = {} |
|---|
| 105 | for value, glyphName in sorted(mapping.items()): |
|---|
| 106 | character = unichr(value) |
|---|
| 107 | # skip whitespace |
|---|
| 108 | if not character.strip(): |
|---|
| 109 | continue |
|---|
| 110 | if glyphName not in glyphNameToCharacter: |
|---|
| 111 | glyphNameToCharacter[glyphName] = [] |
|---|
| 112 | glyphNameToCharacter[glyphName].append(character) |
|---|
| 113 | # use the glyph order defined in the font |
|---|
| 114 | sortedCharacters = [] |
|---|
| 115 | for glyphName in font.getGlyphOrder(): |
|---|
| 116 | if glyphName in glyphNameToCharacter: |
|---|
| 117 | sortedCharacters += glyphNameToCharacter[glyphName] |
|---|
| 118 | return u"".join(sortedCharacters) |
|---|
| 119 | |
|---|
| 120 | # --------------- |
|---|
| 121 | # Public Function |
|---|
| 122 | # --------------- |
|---|
| 123 | |
|---|
| 124 | def proofFont(font, fontPath, sampleText=None): |
|---|
| 125 | """ |
|---|
| 126 | Create a proof file from the given font. This always |
|---|
| 127 | returns HTML. |
|---|
| 128 | |
|---|
| 129 | Arguments: |
|---|
| 130 | **font** - A *WOFFFont* object from *woffLib*. |
|---|
| 131 | **fontPath** - The location of the font file. At the least, this should be the file name for the font. |
|---|
| 132 | **sampleText** - A string of text to display. If not provided, no text will be displayed. |
|---|
| 133 | """ |
|---|
| 134 | # start the html |
|---|
| 135 | title = "Proof: %s" % os.path.basename(fontPath) |
|---|
| 136 | cssReplacements = { |
|---|
| 137 | "/* proof: @font-face rule */" : makeFontFaceRule(font, fontPath, doLocalSrc=False), |
|---|
| 138 | "/* proof: @font-face font-family */" : makeFontFaceFontFamily(font) |
|---|
| 139 | } |
|---|
| 140 | writer = startHTML(title=title, cssReplacements=cssReplacements) |
|---|
| 141 | # file info |
|---|
| 142 | writeFileInfo(font, fontPath, writer) |
|---|
| 143 | # character set |
|---|
| 144 | writeCharacterSet(font, writer, sampleText=sampleText) |
|---|
| 145 | # finish the html |
|---|
| 146 | text = finishHTML(writer) |
|---|
| 147 | text = text.replace("%%break%%", "</br>") |
|---|
| 148 | # return |
|---|
| 149 | return text |
|---|
| 150 | |
|---|
| 151 | # -------------------- |
|---|
| 152 | # Command Line Behvior |
|---|
| 153 | # -------------------- |
|---|
| 154 | |
|---|
| 155 | usage = "%prog [options] fontpath1 fontpath2" |
|---|
| 156 | |
|---|
| 157 | description = """This tool displays information about the |
|---|
| 158 | contents of one or more WOFF files. |
|---|
| 159 | """ |
|---|
| 160 | |
|---|
| 161 | defaultSampleText = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG. The quick brown fox jumps over the lazy dog." |
|---|
| 162 | |
|---|
| 163 | def main(): |
|---|
| 164 | parser = optparse.OptionParser(usage=usage, description=description, version="%prog 0.1beta") |
|---|
| 165 | parser.add_option("-d", dest="outputDirectory", help="Output directory. The default is to output the proof into the same directory as the font file.") |
|---|
| 166 | parser.add_option("-o", dest="outputFileName", help="Output file name. The default is \"fontfilename_proof.html\".") |
|---|
| 167 | parser.add_option("-t", dest="sampleTextFile", help="Sample text file. A file containing sample text to display. If not file is provided, The quick brown fox... will be used.") |
|---|
| 168 | parser.set_defaults(excludeTests=[]) |
|---|
| 169 | (options, args) = parser.parse_args() |
|---|
| 170 | outputDirectory = options.outputDirectory |
|---|
| 171 | if outputDirectory is not None and not os.path.exists(outputDirectory): |
|---|
| 172 | print "Directory does not exist:", outputDirectory |
|---|
| 173 | sys.exit() |
|---|
| 174 | sampleText = defaultSampleText |
|---|
| 175 | if options.sampleTextFile: |
|---|
| 176 | if not os.path.exists(options.sampleTextFile): |
|---|
| 177 | print "Sample text file does not exist:", options.sampleTextFile |
|---|
| 178 | sys.exit() |
|---|
| 179 | f = open(options.sampleTextFile, "r") |
|---|
| 180 | sampleText = f.read() |
|---|
| 181 | f.close() |
|---|
| 182 | for fontPath in args: |
|---|
| 183 | if not os.path.exists(fontPath): |
|---|
| 184 | print "File does not exist:", fontPath |
|---|
| 185 | sys.exit() |
|---|
| 186 | else: |
|---|
| 187 | print "Creating Proof: %s..." % fontPath |
|---|
| 188 | fontPath = fontPath.decode("utf-8") |
|---|
| 189 | font = WOFFFont(fontPath) |
|---|
| 190 | html = proofFont(font, fontPath, sampleText=sampleText) |
|---|
| 191 | # make the output file name |
|---|
| 192 | if options.outputFileName is not None: |
|---|
| 193 | fileName = options.outputFileName |
|---|
| 194 | else: |
|---|
| 195 | fileName = os.path.splitext(os.path.basename(fontPath))[0] |
|---|
| 196 | fileName += "_proof.html" |
|---|
| 197 | # make the output directory |
|---|
| 198 | if options.outputDirectory is not None: |
|---|
| 199 | directory = options.outputDirectory |
|---|
| 200 | else: |
|---|
| 201 | directory = os.path.dirname(fontPath) |
|---|
| 202 | # write the file |
|---|
| 203 | path = os.path.join(directory, fileName) |
|---|
| 204 | path = findUniqueFileName(path) |
|---|
| 205 | f = open(path, "wb") |
|---|
| 206 | f.write(html) |
|---|
| 207 | f.close() |
|---|
| 208 | |
|---|
| 209 | if __name__ == "__main__": |
|---|
| 210 | main() |
|---|