示例#1
0
	def test_stringifyattrs(self):
		writer = XMLWriter(BytesIO())
		expected = ' attr="0"'
		self.assertEqual(expected, writer.stringifyattrs(attr=0))
		self.assertEqual(expected, writer.stringifyattrs(attr=b'0'))
		self.assertEqual(expected, writer.stringifyattrs(attr='0'))
		self.assertEqual(expected, writer.stringifyattrs(attr=u'0'))
示例#2
0
def test_sparse_mmotf(tmpdir):
    base = "%s/sparse_masters" % DATA_DIR
    paths = sorted(glob.glob(base + "/*.otf"))
    # the reference font is modified in-place, make a temp copy first
    # MasterSet_Kanji-w0.00.otf has to be the reference font.
    reference = make_temp_copy(tmpdir, paths[0])
    inpaths = paths[1:]
    outpaths = [str(tmpdir / basename(p)) + ".out" for p in inpaths]

    options = Options(reference, inpaths, outpaths)
    options.allow_no_blues = True
    hintFiles(options)

    refs = [p + ".ref" for p in paths]
    for ref, out in zip(refs, [reference] + outpaths):
        for path in (ref, out):
            font = TTFont(path)
            assert "CFF " in font
            writer = XMLWriter(str(tmpdir / basename(path)) + ".xml")
            font["CFF "].toXML(writer, font)
            writer.close()

        assert differ([
            str(tmpdir / basename(ref)) + ".xml",
            str(tmpdir / basename(out)) + ".xml"
        ])
	def test_dumphex(self):
		writer = XMLWriter(BytesIO())
		writer.dumphex("Type is a beautiful group of letters, not a group of beautiful letters.")
		self.assertEqual(HEADER + bytesjoin([
		    "54797065 20697320 61206265 61757469",
		    "66756c20 67726f75 70206f66 206c6574",
		    "74657273 2c206e6f 74206120 67726f75",
		    "70206f66 20626561 75746966 756c206c",
		    "65747465 72732e  ", ""], joiner=linesep), writer.file.getvalue())
示例#4
0
def getXML(func, ttFont=None):
    """Call the passed toXML function and return the written content as string.
    Result is stripped of XML declaration and OS-specific newline characters.
    """
    writer = XMLWriter(BytesIO())
    # don't write OS-specific new lines
    writer.newlinestr = writer.totype('')
    # erase XML declaration
    writer.file.seek(0)
    writer.file.truncate()
    func(writer, ttFont)
    xml = writer.file.getvalue().decode("utf-8")
    return xml
示例#5
0
def getXML(obj, ttFont):
    """Call the object's toXML() method and return the writer's content as string.
    Result is stripped of XML declaration and OS-specific newline characters.
    """
    writer = XMLWriter(BytesIO())
    # don't write OS-specific new lines
    writer.newlinestr = writer.totype('')
    # erase XML declaration
    writer.file.seek(0)
    writer.file.truncate()
    obj.toXML(writer, ttFont)
    xml = writer.file.getvalue().decode("utf-8")
    return xml
示例#6
0
 def _newPage(self, width, height):
     if hasattr(self, "_svgContext"):
         self._svgContext.endtag("svg")
     self.reset()
     self.size(width, height)
     self._svgData = self._svgFileClass()
     self._pages.append(self._svgData)
     self._svgContext = XMLWriter(self._svgData, encoding="utf-8", indentwhite=self.indentation)
     self._svgContext.width = self.width
     self._svgContext.height = self.height
     attrs = [('width', self.width), ('height', self.height), ('viewBox', f"0 0 {self.width} {self.height}")]
     self._svgContext.begintag("svg", attrs + self._svgTagArguments)
     self._svgContext.newline()
     self._state.transformMatrix = self._state.transformMatrix.scale(1, -1).translate(0, -self.height)
示例#7
0
def test_otf(otf, tmpdir):
    out = str(tmpdir / basename(otf)) + ".out"
    options = Options(otf, out)
    hintFiles(options)

    for path in (otf, out):
        font = TTFont(path)
        assert "CFF " in font
        writer = XMLWriter(str(tmpdir / basename(path)) + ".xml")
        font["CFF "].toXML(writer, font)
        writer.close()

    assert differ([str(tmpdir / basename(otf)) + ".xml",
                   str(tmpdir / basename(out)) + ".xml"])
示例#8
0
 def test_begintag_endtag(self):
     writer = XMLWriter(BytesIO())
     writer.begintag("tag", attr="value")
     writer.write("content")
     writer.endtag("tag")
     self.assertEqual(HEADER + b'<tag attr="value">content</tag>',
                      writer.file.getvalue())
示例#9
0
def test_cff(cff, tmpdir):
    out = str(tmpdir / basename(cff)) + ".out"
    options = Options(cff, out)
    hintFiles(options)

    for path in (cff, out):
        font = CFFFontSet()
        writer = XMLWriter(str(tmpdir / basename(path)) + ".xml")
        with open(path, "rb") as fp:
            font.decompile(fp, None)
            font.toXML(writer)
        writer.close()

    assert differ([str(tmpdir / basename(cff)) + ".xml",
                   str(tmpdir / basename(out)) + ".xml"])
示例#10
0
    def test_newlinestr(self):
        header = b'<?xml version="1.0" encoding="UTF-8"?>'

        for nls in (None, '\n', '\r\n', '\r', ''):
            writer = XMLWriter(BytesIO(), newlinestr=nls)
            writer.write("hello")
            writer.newline()
            writer.write("world")
            writer.newline()

            linesep = tobytes(os.linesep) if nls is None else tobytes(nls)

            self.assertEqual(
                header + linesep + b"hello" + linesep + b"world" + linesep,
                writer.file.getvalue())
示例#11
0
def test_vfotf(otf, tmpdir):
    out = str(tmpdir / basename(otf)) + ".out"
    options = Options(None, [otf], [out])
    options.allow_no_blues = True
    hintFiles(options)

    for path in (otf, out):
        font = TTFont(path)
        assert "CFF2" in font
        writer = XMLWriter(str(tmpdir / basename(path)) + ".xml")
        font["CFF2"].toXML(writer, font)
        writer.close()
    assert differ([
        str(tmpdir / basename(otf)) + ".xml",
        str(tmpdir / basename(out)) + ".xml"
    ])
示例#12
0
    def __init__(self, metaDataPath, fontPath):
        self.writer = XMLWriter(metaDataPath, encoding="UTF-8")
        self.font = Font(fontPath)

        self.beginMetaData()

        self.uniqueId()
        self.vendor()
        self.credits()
        self.description()
        self.license()
        self.copyright()
        self.trademark()
        self.endMetaData()

        self.writer.close()
示例#13
0
def makeXMLWriter(newlinestr='\n'):
    # don't write OS-specific new lines
    writer = XMLWriter(BytesIO(), newlinestr=newlinestr)
    # erase XML declaration
    writer.file.seek(0)
    writer.file.truncate()
    return writer
示例#14
0
def test_decimals_otf(tmpdir):
    otf = "%s/dummy/decimals.otf" % DATA_DIR
    out = str(tmpdir / basename(otf)) + ".out"
    options = Options(otf, out)
    options.round_coords = False

    hintFiles(options)

    for path in (otf, out):
        font = TTFont(path)
        assert "CFF " in font
        writer = XMLWriter(str(tmpdir / basename(path)) + ".xml")
        font["CFF "].toXML(writer, font)
        writer.close()

    assert differ([str(tmpdir / basename(otf)) + ".xml",
                   str(tmpdir / basename(out)) + ".xml"])
 def test_toXML(self):
     table = table__m_e_t_a()
     table.data["TEST"] = b"\xCA\xFE\xBE\xEF"
     writer = XMLWriter(BytesIO())
     table.toXML(writer, {"meta": table})
     xml = writer.file.getvalue().decode("utf-8")
     self.assertEqual(['<hexdata tag="TEST">', 'cafebeef', '</hexdata>'],
                      [line.strip() for line in xml.splitlines()][1:])
 def test_toXML_text(self):
     table = table__m_e_t_a()
     table.data["dlng"] = u"Latn,Grek,Cyrl"
     writer = XMLWriter(BytesIO())
     table.toXML(writer, {"meta": table})
     xml = writer.file.getvalue().decode("utf-8")
     self.assertEqual(['<text tag="dlng">', 'Latn,Grek,Cyrl', '</text>'],
                      [line.strip() for line in xml.splitlines()][1:])
示例#17
0
 def test_toXML_allDeltasNone(self):
     writer = XMLWriter(BytesIO())
     axes = {"wght": (0.0, 1.0, 1.0)}
     g = GlyphVariation(axes, [None] * 5)
     g.toXML(writer, ["wght", "wdth"])
     self.assertEqual([
         '<tuple>', '<coord axis="wght" value="1.0"/>',
         '<!-- no deltas -->', '</tuple>'
     ], GlyphVariationTest.xml_lines(writer))
示例#18
0
 def test_toXML2(self):
     writer = XMLWriter(StringIO())
     table = otTables.MultipleSubst()
     table.mapping = {"c_t": ["c", "t"], "f_f_i": ["f", "f", "i"]}
     table.toXML2(writer, self.font)
     self.assertEqual(writer.file.getvalue().splitlines()[1:], [
         '<Substitution in="c_t" out="c,t"/>',
         '<Substitution in="f_f_i" out="f,f,i"/>',
     ])
示例#19
0
	def test_newlinestr(self):
		header = b'<?xml version="1.0" encoding="UTF-8"?>'

		for nls in (None, '\n', '\r\n', '\r', ''):
			writer = XMLWriter(BytesIO(), newlinestr=nls)
			writer.write("hello")
			writer.newline()
			writer.write("world")
			writer.newline()

			linesep = tobytes(os.linesep) if nls is None else tobytes(nls)

			self.assertEqual(
				header + linesep + b"hello" + linesep + b"world" + linesep,
				writer.file.getvalue())
示例#20
0
 def test_toXML_ascii_data(self):
     table = table__m_e_t_a()
     table.data["TEST"] = b"Hello!"
     writer = XMLWriter(BytesIO())
     table.toXML(writer, {"meta": table})
     xml = writer.file.getvalue().decode("utf-8")
     self.assertEqual([
         '<hexdata tag="TEST">', '<!-- ascii: Hello! -->', '48656c6c 6f21',
         '</hexdata>'
     ], [line.strip() for line in xml.splitlines()][1:])
示例#21
0
 def test_toXML2(self):
     writer = XMLWriter(StringIO())
     table = otTables.SingleSubst()
     table.mapping = {"A": "a", "B": "b", "C": "c"}
     table.toXML2(writer, self.font)
     self.assertEqual(writer.file.getvalue().splitlines()[1:], [
         '<Substitution in="A" out="a"/>',
         '<Substitution in="B" out="b"/>',
         '<Substitution in="C" out="c"/>',
     ])
示例#22
0
 def test_toXML(self):
     avar = table__a_v_a_r()
     avar.segments["opsz"] = {-1.0: -1.0, 0.0: 0.0, 0.3: 0.8, 1.0: 1.0}
     writer = XMLWriter(StringIO())
     avar.toXML(writer, self.makeFont(["opsz"]))
     self.assertEqual([
         '<segment axis="opsz">', '<mapping from="-1.0" to="-1.0"/>',
         '<mapping from="0.0" to="0.0"/>', '<mapping from="0.3" to="0.8"/>',
         '<mapping from="1.0" to="1.0"/>', '</segment>'
     ], self.xml_lines(writer))
示例#23
0
 def test_toXML(self):
     writer = XMLWriter(StringIO())
     table = table__l_t_a_g()
     table.decompile(self.DATA_, ttFont=None)
     table.toXML(writer, ttFont=None)
     expected = "\n".join([
         '<?xml version="1.0" encoding="UTF-8"?>', '<version value="1"/>',
         '<flags value="0"/>', '<LanguageTag tag="en"/>',
         '<LanguageTag tag="zh-Hant"/>', '<LanguageTag tag="zh"/>'
     ]) + "\n"
     self.assertEquals(expected.encode("utf_8"), writer.file.getvalue())
示例#24
0
 def test_toXML(self):
     writer = XMLWriter(BytesIO())
     table = newTable("ltag")
     table.decompile(self.DATA_, ttFont=None)
     table.toXML(writer, ttFont=None)
     expected = os.linesep.join([
         '<?xml version="1.0" encoding="UTF-8"?>', '<version value="1"/>',
         '<flags value="0"/>', '<LanguageTag tag="en"/>',
         '<LanguageTag tag="zh-Hant"/>', '<LanguageTag tag="zh"/>'
     ]) + os.linesep
     self.assertEqual(expected.encode("utf_8"), writer.file.getvalue())
示例#25
0
 def test_toXML2(self):
     writer = XMLWriter(StringIO())
     table = otTables.AlternateSubst()
     table.alternates = {"G": ["G.alt2", "G.alt1"], "Z": ["Z.fina"]}
     table.toXML2(writer, self.font)
     self.assertEqual(writer.file.getvalue().splitlines()[1:], [
         '<AlternateSet glyph="G">', '  <Alternate glyph="G.alt2"/>',
         '  <Alternate glyph="G.alt1"/>', '</AlternateSet>',
         '<AlternateSet glyph="Z">', '  <Alternate glyph="Z.fina"/>',
         '</AlternateSet>'
     ])
示例#26
0
 def test_toXML_constants(self):
     writer = XMLWriter(BytesIO())
     g = TupleVariation(AXES, [42, None, 23, 0, -17, None])
     g.toXML(writer, ["wdth", "wght", "opsz"])
     self.assertEqual([
         '<tuple>', '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
         '<coord axis="wght" value="1.0"/>',
         '<coord axis="opsz" value="-0.7"/>', '<delta cvt="0" value="42"/>',
         '<delta cvt="2" value="23"/>', '<delta cvt="3" value="0"/>',
         '<delta cvt="4" value="-17"/>', '</tuple>'
     ], TupleVariationTest.xml_lines(writer))
示例#27
0
 def test_toXML(self):
     font = MakeFont()
     writer = XMLWriter(BytesIO())
     font["fvar"].toXML(writer, font)
     xml = writer.file.getvalue().decode("utf-8")
     self.assertEqual(2, xml.count("<Axis>"))
     self.assertTrue("<AxisTag>wght</AxisTag>" in xml)
     self.assertTrue("<AxisTag>wdth</AxisTag>" in xml)
     self.assertEqual(2, xml.count("<NamedInstance "))
     self.assertTrue("<!-- Light -->" in xml)
     self.assertTrue("<!-- Light Condensed -->" in xml)
示例#28
0
 def test_toXML_points(self):
     writer = XMLWriter(BytesIO())
     g = TupleVariation(AXES, [(9, 8), None, (7, 6), (0, 0),
                               (-1, -2), None])
     g.toXML(writer, ["wdth", "wght", "opsz"])
     self.assertEqual([
         '<tuple>', '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
         '<coord axis="wght" value="1.0"/>',
         '<coord axis="opsz" value="-0.7"/>', '<delta pt="0" x="9" y="8"/>',
         '<delta pt="2" x="7" y="6"/>', '<delta pt="3" x="0" y="0"/>',
         '<delta pt="4" x="-1" y="-2"/>', '</tuple>'
     ], TupleVariationTest.xml_lines(writer))
示例#29
0
	def test_toXML_badDeltaFormat(self):
		writer = XMLWriter(BytesIO())
		g = TupleVariation(AXES, ["String"])
		with CapturingLogHandler(log, "ERROR") as captor:
			g.toXML(writer, ["wdth"])
		self.assertIn("bad delta format", [r.msg for r in captor.records])
		self.assertEqual([
			'<tuple>',
			  '<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>',
			  '<!-- bad delta #0 -->',
			'</tuple>',
		], TupleVariationTest.xml_lines(writer))
示例#30
0
 def test_stringifyattrs(self):
     writer = XMLWriter(BytesIO())
     expected = ' attr="0"'
     self.assertEqual(expected, writer.stringifyattrs(attr=0))
     self.assertEqual(expected, writer.stringifyattrs(attr=b'0'))
     self.assertEqual(expected, writer.stringifyattrs(attr='0'))
     self.assertEqual(expected, writer.stringifyattrs(attr=u'0'))
示例#31
0
 def test_xml_indentation(self):
     with open(TTPROGRAM_TTX, 'r', encoding='utf-8') as f:
         ttProgramXML = f.read()
     p = Program()
     p.fromBytecode(BYTECODE)
     ttfont = TestFont()
     buf = StringIO()
     writer = XMLWriter(buf)
     try:
         p.toXML(writer, ttfont)
     finally:
         output_string = buf.getvalue()
     assert output_string == ttProgramXML
 def _newPage(self, width, height):
     if hasattr(self, "_svgContext"):
         self._svgContext.endtag("svg")
     self.reset()
     self.size(width, height)
     self._svgData = self._svgFileClass()
     self._pages.append(self._svgData)
     self._svgContext = XMLWriter(self._svgData, encoding="utf-8", indentwhite=self.indentation)
     self._svgContext.width = self.width
     self._svgContext.height = self.height
     self._svgContext.begintag("svg", width=self.width, height=self.height, **self._svgTagArguments)
     self._svgContext.newline()
     self._state.transformMatrix = self._state.transformMatrix.scale(1, -1).translate(0, -self.height)
示例#33
0
 def test_toXML(self):
     font = MakeFont()
     inst = NamedInstance()
     inst.nameID = AddName(font, "Light Condensed").nameID
     inst.coordinates = {"wght": 0.7, "wdth": 0.5}
     writer = XMLWriter(StringIO())
     inst.toXML(writer, font)
     self.assertEqual([
         '', '<!-- Light Condensed -->',
         '<NamedInstance nameID="%s">' % inst.nameID,
         '<coord axis="wght" value="0.7"/>',
         '<coord axis="wdth" value="0.5"/>', '</NamedInstance>'
     ], xml_lines(writer))
示例#34
0
class SVGContext(BaseContext):

    _graphicsStateClass = SVGGraphicsState
    _shadowClass = SVGShadow
    _colorClass = SVGColor
    _gradientClass = SVGGradient

    _svgFileClass = SVGFile

    _svgTagArguments = {
        "version": "1.1",
        "xmlns": "http://www.w3.org/2000/svg",
        }

    _svgLineJoinStylesMap = {
                    AppKit.NSMiterLineJoinStyle: "miter",
                    AppKit.NSRoundLineJoinStyle: "round",
                    AppKit.NSBevelLineJoinStyle: "bevel"
                    }

    _svgLineCapStylesMap = {
        AppKit.NSButtLineCapStyle: "butt",
        AppKit.NSSquareLineCapStyle: "square",
        AppKit.NSRoundLineCapStyle: "round",
    }

    indentation = " "
    fileExtensions = ["svg"]

    def __init__(self):
        super(SVGContext, self).__init__()
        self._pages = []

    # not supported in a svg context

    def cmykFill(self, c, m, y, k, a=1):
        warnings.warn("cmykFill is not supported in a svg context")

    def cmykStroke(self, c, m, y, k, a=1):
        warnings.warn("cmykStroke is not supported in a svg context")

    def cmykLinearGradient(self, startPoint=None, endPoint=None, colors=None, locations=None):
        warnings.warn("cmykLinearGradient is not supported in a svg context")

    def cmykRadialGradient(self, startPoint=None, endPoint=None, colors=None, locations=None, startRadius=0, endRadius=100):
        warnings.warn("cmykRadialGradient is not supported in a svg context")

    def cmykShadow(self, offset, blur, color):
        warnings.warn("cmykShadow is not supported in a svg context")

    # svg overwrites

    def shadow(self, offset, blur, color):
        super(SVGContext, self).shadow(offset, blur, color)
        if self._state.shadow is not None:
            self._state.shadow.writeDefs(self._svgContext)

    def linearGradient(self, startPoint=None, endPoint=None, colors=None, locations=None):
        super(SVGContext, self).linearGradient(startPoint, endPoint, colors, locations)
        if self._state.gradient is not None:
            self._state.gradient.writeDefs(self._svgContext)

    def radialGradient(self, startPoint=None, endPoint=None, colors=None, locations=None, startRadius=0, endRadius=100):
        super(SVGContext, self).radialGradient(startPoint, endPoint, colors, locations, startRadius, endRadius)
        if startRadius != 0:
            warnings.warn("radialGradient will clip the startRadius to '0' in a svg context.")
        if self._state.gradient is not None:
            self._state.gradient.writeDefs(self._svgContext)

    # svg

    def _reset(self, other=None):
        self._embeddedFonts = set()

    def _newPage(self, width, height):
        if hasattr(self, "_svgContext"):
            self._svgContext.endtag("svg")
        self.reset()
        self.size(width, height)
        self._svgData = self._svgFileClass()
        self._pages.append(self._svgData)
        self._svgContext = XMLWriter(self._svgData, encoding="utf-8", indentwhite=self.indentation)
        self._svgContext.width = self.width
        self._svgContext.height = self.height
        self._svgContext.begintag("svg", width=self.width, height=self.height, **self._svgTagArguments)
        self._svgContext.newline()
        self._state.transformMatrix = self._state.transformMatrix.scale(1, -1).translate(0, -self.height)

    def _saveImage(self, path, multipage):
        if multipage is None:
            multipage = False
        self._svgContext.endtag("svg")
        fileName, fileExt = os.path.splitext(path)
        firstPage = 0
        pageCount = len(self._pages)
        pathAdd = "_1"
        if not multipage:
            firstPage = pageCount - 1
            pathAdd = ""
        for index in range(firstPage, pageCount):
            page = self._pages[index]
            svgPath = fileName + pathAdd + fileExt
            page.writeToFile(svgPath)
            pathAdd = "_%s" % (index + 2)

    def _save(self):
        pass

    def _restore(self):
        pass

    def _drawPath(self):
        if self._state.path:
            self._svgBeginClipPath()
            data = self._svgDrawingAttributes()
            data["d"] = self._svgPath(self._state.path)
            data["transform"] = self._svgTransform(self._state.transformMatrix)
            if self._state.shadow is not None:
                data["filter"] = "url(#%s)" % self._state.shadow.tagID
            if self._state.gradient is not None:
                data["fill"] = "url(#%s)" % self._state.gradient.tagID
            self._svgContext.simpletag("path", **data)
            self._svgContext.newline()
            self._svgEndClipPath()

    def _clipPath(self):
        uniqueID = self._getUniqueID()
        self._svgContext.begintag("clipPath", id=uniqueID)
        self._svgContext.newline()
        data = dict()
        data["d"] = self._svgPath(self._state.path)
        data["transform"] = self._svgTransform(self._state.transformMatrix)
        data["clip-rule"] = "evenodd"
        self._svgContext.simpletag("path", **data)
        self._svgContext.newline()
        self._svgContext.endtag("clipPath")
        self._svgContext.newline()
        self._state.clipPathID = uniqueID

    def _textBox(self, txt, box, align):
        path, (x, y) = self._getPathForFrameSetter(box)
        canDoGradients = True
        if align == "justified":
            warnings.warn("justified text is not supported in a svg context")
        attrString = self.attributedString(txt, align=align)
        if self._state.hyphenation:
            attrString = self.hyphenateAttributedString(attrString, path)
        txt = attrString.string()
        setter = CoreText.CTFramesetterCreateWithAttributedString(attrString)
        box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None)

        self._svgBeginClipPath()
        defaultData = self._svgDrawingAttributes()

        data = {
            "text-anchor": "start",
            "transform": self._svgTransform(self._state.transformMatrix.translate(x, y + self.height).scale(1, -1))
            }
        if self._state.shadow is not None:
            data["filter"] = "url(#%s_flipped)" % self._state.shadow.tagID
        self._svgContext.begintag("text", **data)
        self._svgContext.newline()

        ctLines = CoreText.CTFrameGetLines(box)
        origins = CoreText.CTFrameGetLineOrigins(box, (0, len(ctLines)), None)
        for i, (originX, originY) in enumerate(origins):
            ctLine = ctLines[i]
            # bounds = CoreText.CTLineGetImageBounds(ctLine, self._pdfContext)
            # if bounds.size.width == 0:
            #     continue
            ctRuns = CoreText.CTLineGetGlyphRuns(ctLine)
            for ctRun in ctRuns:
                stringRange = CoreText.CTRunGetStringRange(ctRun)
                attributes = CoreText.CTRunGetAttributes(ctRun)
                font = attributes.get(AppKit.NSFontAttributeName)
                fontAttributes = font.fontDescriptor().fontAttributes()
                fillColor = attributes.get(AppKit.NSForegroundColorAttributeName)
                strokeColor = attributes.get(AppKit.NSStrokeColorAttributeName)
                strokeWidth = attributes.get(AppKit.NSStrokeWidthAttributeName, self._state.strokeWidth)
                baselineShift = attributes.get(AppKit.NSBaselineOffsetAttributeName, 0)
                openTypeFeatures = fontAttributes.get(CoreText.NSFontFeatureSettingsAttribute)

                fontName = font.fontName()
                fontSize = font.pointSize()

                spanData = dict(defaultData)
                fill = self._colorClass(fillColor).svgColor()
                if fill:
                    spanData["fill"] = fill
                stroke = self._colorClass(strokeColor).svgColor()
                if stroke:
                    spanData["stroke"] = stroke
                    spanData["stroke-width"] = formatNumber(abs(strokeWidth))
                spanData["font-family"] = fontName
                spanData["font-size"] = formatNumber(fontSize)

                if openTypeFeatures:
                    featureTags = getFeatureTagsForFontAttributes(openTypeFeatures)
                    spanData["style"] = self._svgStyle(**{
                            "font-feature-settings": self._svgStyleOpenTypeFeatures(featureTags)
                        }
                    )

                if canDoGradients and self._state.gradient is not None:
                    spanData["fill"] = "url(#%s_flipped)" % self._state.gradient.tagID

                self._save()

                runTxt = txt.substringWithRange_((stringRange.location, stringRange.length))
                while runTxt and runTxt[-1] == " ":
                    runTxt = runTxt[:-1]
                runTxt = runTxt.replace("\n", "")
                runTxt = runTxt.encode("utf-8")

                runPos = CoreText.CTRunGetPositions(ctRun, (0, 1), None)
                runX = runY = 0
                if runPos:
                    runX = runPos[0].x
                    runY = runPos[0].y

                spanData["x"] = formatNumber(originX + runX)
                spanData["y"] = formatNumber(self.height - originY - runY + baselineShift)
                self._svgContext.begintag("tspan", **spanData)
                self._svgContext.newline()
                self._svgContext.write(runTxt)
                self._svgContext.newline()
                self._svgContext.endtag("tspan")
                self._svgContext.newline()
                self._restore()

        self._svgContext.endtag("text")
        self._svgContext.newline()
        self._svgEndClipPath()

    def _image(self, path, (x, y), alpha, pageNumber):
        # todo:
        # support embedding of images when the source is not a path but
        # a nsimage or a pdf / gif with a pageNumber
        self._svgBeginClipPath()
        if path.startswith("http"):
            url = AppKit.NSURL.URLWithString_(path)
        else:
            url = AppKit.NSURL.fileURLWithPath_(path)
        im = AppKit.NSImage.alloc().initByReferencingURL_(url)
        w, h = im.size()
        data = dict()
        data["x"] = 0
        data["y"] = 0
        data["width"] = w
        data["height"] = h
        data["opacity"] = alpha
        data["transform"] = self._svgTransform(self._state.transformMatrix.translate(x, y + h).scale(1, -1))
        data["xlink:href"] = path
        self._svgContext.simpletag("image", **data)
        self._svgContext.newline()
        self._svgEndClipPath()
class SVGContext(BaseContext):

    _graphicsStateClass = SVGGraphicsState
    _shadowClass = SVGShadow
    _colorClass = SVGColor
    _gradientClass = SVGGradient
    _clipPathIDGenerator = _UniqueIDGenerator("clip")

    _svgFileClass = SVGFile

    _svgTagArguments = {
        "version": "1.1",
        "xmlns": "http://www.w3.org/2000/svg",
        "xmlns:xlink": "http://www.w3.org/1999/xlink"
    }

    _svgLineJoinStylesMap = {
        AppKit.NSMiterLineJoinStyle: "miter",
        AppKit.NSRoundLineJoinStyle: "round",
        AppKit.NSBevelLineJoinStyle: "bevel"
    }

    _svgLineCapStylesMap = {
        AppKit.NSButtLineCapStyle: "butt",
        AppKit.NSSquareLineCapStyle: "square",
        AppKit.NSRoundLineCapStyle: "round",
    }

    indentation = " "
    fileExtensions = ["svg"]
    saveImageOptions = [
        ("multipage", "Output a numbered svg file for each page or frame in the document."),
    ]

    def __init__(self):
        super(SVGContext, self).__init__()
        self._pages = []

    # not supported in a svg context

    def cmykFill(self, c, m, y, k, a=1):
        warnings.warn("cmykFill is not supported in a svg context")

    def cmykStroke(self, c, m, y, k, a=1):
        warnings.warn("cmykStroke is not supported in a svg context")

    def cmykLinearGradient(self, startPoint=None, endPoint=None, colors=None, locations=None):
        warnings.warn("cmykLinearGradient is not supported in a svg context")

    def cmykRadialGradient(self, startPoint=None, endPoint=None, colors=None, locations=None, startRadius=0, endRadius=100):
        warnings.warn("cmykRadialGradient is not supported in a svg context")

    def cmykShadow(self, offset, blur, color):
        warnings.warn("cmykShadow is not supported in a svg context")

    # svg overwrites

    def shadow(self, offset, blur, color):
        super(SVGContext, self).shadow(offset, blur, color)
        if self._state.shadow is not None:
            self._state.shadow.writeDefs(self._svgContext)

    def linearGradient(self, startPoint=None, endPoint=None, colors=None, locations=None):
        super(SVGContext, self).linearGradient(startPoint, endPoint, colors, locations)
        if self._state.gradient is not None:
            self._state.gradient.writeDefs(self._svgContext)

    def radialGradient(self, startPoint=None, endPoint=None, colors=None, locations=None, startRadius=0, endRadius=100):
        super(SVGContext, self).radialGradient(startPoint, endPoint, colors, locations, startRadius, endRadius)
        if startRadius != 0:
            warnings.warn("radialGradient will clip the startRadius to '0' in a svg context.")
        if self._state.gradient is not None:
            self._state.gradient.writeDefs(self._svgContext)

    # svg

    def _reset(self, other=None):
        self._embeddedFonts = set()
        self._embeddedImages = dict()

    def _newPage(self, width, height):
        if hasattr(self, "_svgContext"):
            self._svgContext.endtag("svg")
        self.reset()
        self.size(width, height)
        self._svgData = self._svgFileClass()
        self._pages.append(self._svgData)
        self._svgContext = XMLWriter(self._svgData, encoding="utf-8", indentwhite=self.indentation)
        self._svgContext.width = self.width
        self._svgContext.height = self.height
        self._svgContext.begintag("svg", width=self.width, height=self.height, **self._svgTagArguments)
        self._svgContext.newline()
        self._state.transformMatrix = self._state.transformMatrix.scale(1, -1).translate(0, -self.height)

    def _saveImage(self, path, options):
        multipage = options.get("multipage")
        if multipage is None:
            multipage = False
        self._svgContext.endtag("svg")
        fileName, fileExt = os.path.splitext(path)
        firstPage = 0
        pageCount = len(self._pages)
        pathAdd = "_1"
        if not multipage:
            firstPage = pageCount - 1
            pathAdd = ""
        for index in range(firstPage, pageCount):
            page = self._pages[index]
            svgPath = fileName + pathAdd + fileExt
            page.writeToFile(svgPath)
            pathAdd = "_%s" % (index + 2)

    def _save(self):
        pass

    def _restore(self):
        pass

    def _drawPath(self):
        if self._state.path:
            self._svgBeginClipPath()
            data = self._svgDrawingAttributes()
            data["d"] = self._svgPath(self._state.path)
            data["transform"] = self._svgTransform(self._state.transformMatrix)
            if self._state.shadow is not None:
                data["filter"] = "url(#%s)" % self._state.shadow.tagID
            if self._state.gradient is not None:
                data["fill"] = "url(#%s)" % self._state.gradient.tagID
            self._svgContext.simpletag("path", **data)
            self._svgContext.newline()
            self._svgEndClipPath()

    def _clipPath(self):
        uniqueID = self._clipPathIDGenerator.gen()
        self._svgContext.begintag("clipPath", id=uniqueID)
        self._svgContext.newline()
        data = dict()
        data["d"] = self._svgPath(self._state.path)
        data["transform"] = self._svgTransform(self._state.transformMatrix)
        data["clip-rule"] = "evenodd"
        self._svgContext.simpletag("path", **data)
        self._svgContext.newline()
        self._svgContext.endtag("clipPath")
        self._svgContext.newline()
        self._state.clipPathID = uniqueID

    def _textBox(self, txt, box, align):
        path, (x, y) = self._getPathForFrameSetter(box)
        canDoGradients = True
        if align == "justified":
            warnings.warn("justified text is not supported in a svg context")
        attrString = self.attributedString(txt, align=align)
        if self._state.hyphenation:
            attrString = self.hyphenateAttributedString(attrString, path)
        txt = attrString.string()
        setter = CoreText.CTFramesetterCreateWithAttributedString(attrString)
        box = CoreText.CTFramesetterCreateFrame(setter, (0, 0), path, None)

        self._svgBeginClipPath()
        defaultData = self._svgDrawingAttributes()

        data = {
            "text-anchor": "start",
            "transform": self._svgTransform(self._state.transformMatrix.translate(x, y + self.height).scale(1, -1))
        }
        if self._state.shadow is not None:
            data["filter"] = "url(#%s_flipped)" % self._state.shadow.tagID
        self._svgContext.begintag("text", **data)
        self._svgContext.newline()

        ctLines = CoreText.CTFrameGetLines(box)
        origins = CoreText.CTFrameGetLineOrigins(box, (0, len(ctLines)), None)
        for i, (originX, originY) in enumerate(origins):
            ctLine = ctLines[i]
            # bounds = CoreText.CTLineGetImageBounds(ctLine, self._pdfContext)
            # if bounds.size.width == 0:
            #     continue
            ctRuns = CoreText.CTLineGetGlyphRuns(ctLine)
            for ctRun in ctRuns:
                stringRange = CoreText.CTRunGetStringRange(ctRun)
                attributes = CoreText.CTRunGetAttributes(ctRun)
                font = attributes.get(AppKit.NSFontAttributeName)
                fontAttributes = font.fontDescriptor().fontAttributes()
                fillColor = attributes.get(AppKit.NSForegroundColorAttributeName)
                strokeColor = attributes.get(AppKit.NSStrokeColorAttributeName)
                strokeWidth = attributes.get(AppKit.NSStrokeWidthAttributeName, self._state.strokeWidth)
                baselineShift = attributes.get(AppKit.NSBaselineOffsetAttributeName, 0)
                openTypeFeatures = fontAttributes.get(CoreText.NSFontFeatureSettingsAttribute)

                fontName = font.fontName()
                fontSize = font.pointSize()

                spanData = dict(defaultData)
                fill = self._colorClass(fillColor).svgColor()
                if fill:
                    c, a = fill
                    spanData["fill"] = c
                    if a != 1:
                        spanData["fill-opacity"] = a
                stroke = self._colorClass(strokeColor).svgColor()
                if stroke:
                    c, a = stroke
                    spanData["stroke"] = c
                    if a != 1:
                        spanData["stroke-opacity"] = a
                    spanData["stroke-width"] = formatNumber(abs(strokeWidth) * .5)
                spanData["font-family"] = fontName
                spanData["font-size"] = formatNumber(fontSize)

                if openTypeFeatures:
                    featureTags = getFeatureTagsForFontAttributes(openTypeFeatures)
                    spanData["style"] = self._svgStyle(**{
                            "font-feature-settings": self._svgStyleOpenTypeFeatures(featureTags)
                        }
                    )

                if canDoGradients and self._state.gradient is not None:
                    spanData["fill"] = "url(#%s_flipped)" % self._state.gradient.tagID

                self._save()

                runTxt = txt.substringWithRange_((stringRange.location, stringRange.length))
                while runTxt and runTxt[-1] == " ":
                    runTxt = runTxt[:-1]
                runTxt = runTxt.replace("\n", "")
                runTxt = runTxt.encode("utf-8")

                runPos = CoreText.CTRunGetPositions(ctRun, (0, 1), None)
                runX = runY = 0
                if runPos:
                    runX = runPos[0].x
                    runY = runPos[0].y

                spanData["x"] = formatNumber(originX + runX)
                spanData["y"] = formatNumber(self.height - originY - runY + baselineShift)
                self._svgContext.begintag("tspan", **spanData)
                self._svgContext.newline()
                self._svgContext.write(runTxt)
                self._svgContext.newline()
                self._svgContext.endtag("tspan")
                self._svgContext.newline()
                self._restore()

        self._svgContext.endtag("text")
        self._svgContext.newline()
        self._svgEndClipPath()

    def _image(self, path, xy, alpha, pageNumber):
        # todo:
        # support embedding of images when the source is not a path but
        # a nsimage or a pdf / gif with a pageNumber
        x, y = xy
        self._svgBeginClipPath()
        if path.startswith("http"):
            url = AppKit.NSURL.URLWithString_(path)
        else:
            url = AppKit.NSURL.fileURLWithPath_(path)
        image = AppKit.NSImage.alloc().initByReferencingURL_(url)
        width, height = image.size()

        if path not in self._embeddedImages:
            # get a unique id for the image
            imageID = "image_%s" % (len(self._embeddedImages) + 1)
            # store it
            self._embeddedImages[path] = imageID
            _, ext = os.path.splitext(path)
            mimeSubtype = ext[1:].lower()  # remove the dot, make lowercase
            if mimeSubtype == "jpg":
                mimeSubtype = "jpeg"
            if mimeSubtype not in ("png", "jpeg"):
                # the image is not a png or a jpeg
                # convert it to a png
                mimeSubtype = "png"
                imageRep = _makeBitmapImageRep(image)
                imageData = imageRep.representationUsingType_properties_(AppKit.NSPNGFileType, None)
            else:
                imageData = AppKit.NSData.dataWithContentsOfURL_(url).bytes()

            defData = [
                ("id", imageID),
                ("width", width),
                ("height", height),
                ("xlink:href", "data:image/%s;base64,%s" % (mimeSubtype, base64.b64encode(imageData).decode("ascii")))
            ]
            self._svgContext.begintag("defs")
            self._svgContext.newline()
            self._svgContext.simpletag("image", defData)
            self._svgContext.newline()
            self._svgContext.endtag("defs")
            self._svgContext.newline()
        else:
            imageID = self._embeddedImages[path]

        data = [
            ("x", 0),
            ("y", 0),
            ("opacity", alpha),
            ("transform", self._svgTransform(self._state.transformMatrix.translate(x, y + height).scale(1, -1))),
            ("xlink:href", "#%s" % imageID)
        ]
        self._svgContext.simpletag("use", data)
        self._svgContext.newline()
        self._svgEndClipPath()

    def _transform(self, transform):
        self._state.transformMatrix = self._state.transformMatrix.transform(transform)

    # helpers

    def _svgTransform(self, transform):
        return "matrix(%s)" % (",".join([repr(s) for s in transform]))

    def _svgPath(self, path, transformMatrix=None):
        path = path.getNSBezierPath()
        if transformMatrix:
            path = path.copy()
            aT = AppKit.NSAffineTransform.transform()
            aT.setTransformStruct_(transformMatrix[:])
            path.transformUsingAffineTransform_(aT)
        svg = ""
        for i in range(path.elementCount()):
            instruction, points = path.elementAtIndex_associatedPoints_(i)
            if instruction == AppKit.NSMoveToBezierPathElement:
                svg += "M%s,%s " % (formatNumber(points[0].x), formatNumber(points[0].y))
                previousPoint = points[-1]
            elif instruction == AppKit.NSLineToBezierPathElement:
                x = points[0].x - previousPoint.x
                y = points[0].y - previousPoint.y
                svg += "l%s,%s " % (formatNumber(x), formatNumber(y))
                previousPoint = points[-1]
            elif instruction == AppKit.NSCurveToBezierPathElement:
                offx1 = points[0].x - previousPoint.x
                offy1 = points[0].y - previousPoint.y
                offx2 = points[1].x - previousPoint.x
                offy2 = points[1].y - previousPoint.y
                x = points[2].x - previousPoint.x
                y = points[2].y - previousPoint.y
                svg += "c%s,%s,%s,%s,%s,%s " % (formatNumber(offx1), formatNumber(offy1), formatNumber(offx2), formatNumber(offy2), formatNumber(x), formatNumber(y))
                previousPoint = points[-1]
            elif instruction == AppKit.NSClosePathBezierPathElement:
                svg += "Z "
        return svg.strip()

    def _svgBeginClipPath(self):
        if self._state.clipPathID:
            data = dict()
            data["clip-path"] = "url(#%s)" % self._state.clipPathID
            self._svgContext.begintag("g", **data)
            self._svgContext.newline()

    def _svgEndClipPath(self):
        if self._state.clipPathID:
            self._svgContext.endtag("g")
            self._svgContext.newline()

    def _svgDrawingAttributes(self):
        data = dict()
        fill = self._svgFillColor()
        if fill:
            c, a = fill
            data["fill"] = c
            if a != 1:
                data["fill-opacity"] = a
        else:
            data["fill"] = "none"
        stroke = self._svgStrokeColor()
        if stroke:
            c, a = stroke
            data["stroke"] = c
            if a != 1:
                data["stroke-opacity"] = a
            data["stroke-width"] = formatNumber(abs(self._state.strokeWidth))
        if self._state.lineDash:
            data["stroke-dasharray"] = ",".join([str(i) for i in self._state.lineDash])
        if self._state.lineJoin in self._svgLineJoinStylesMap:
            data["stroke-linejoin"] = self._svgLineJoinStylesMap[self._state.lineJoin]
        if self._state.lineCap in self._svgLineCapStylesMap:
            data["stroke-linecap"] = self._svgLineCapStylesMap[self._state.lineCap]
        return data

    def _svgFillColor(self):
        if self._state.fillColor:
            return self._state.fillColor.svgColor()
        return None

    def _svgStrokeColor(self):
        if self._state.strokeColor:
            return self._state.strokeColor.svgColor()
        return None

    def _svgStyleOpenTypeFeatures(self, featureTags):
        return ", ".join(["'%s'" % tag for tag in featureTags])

    def _svgStyle(self, **kwargs):
        style = []
        if self._state.blendMode is not None:
            style.append("mix-blend-mode: %s;" % self._state.blendMode)
        for key, value in kwargs.items():
            style.append("%s: %s;" % (key, value))
        return " ".join(style)

    def installFont(self, path):
        success, error = super(self.__class__, self).installFont(path)
        # if path not in self._embeddedFonts:
        #     warnings.warn("Your font will be embedded and accessibele")
        #     self._embeddedFonts.add(path)

        #     f = open(path, "r")
        #     fontData = f.read()
        #     f.close()
        #     fontName = self._fontNameForPath(path)

        #     ctx = self._svgContext
        #     ctx.begintag("defs")
        #     ctx.newline()
        #     ctx.begintag("style", type="text/css")
        #     ctx.newline()
        #     ctx.write("@font-face {")
        #     ctx.newline()
        #     ctx.indent()
        #     ctx.write("font-family: %s;" % fontName)
        #     ctx.newline()
        #     if path.startswith("http"):
        #         ctx.write("src: url(%s');" % path)
        #     else:
        #         ctx.write("src: url('data:application/font-woff;charset=utf-8;base64,%s');" % base64.b64encode(fontData))
        #     ctx.newline()
        #     ctx.dedent()
        #     ctx.write("}")
        #     ctx.newline()
        #     ctx.endtag("style")
        #     ctx.newline()
        #     ctx.endtag("defs")
        #     ctx.newline()

        return success, error
示例#36
0
def writeGlyphToString(glyphName, glyphObject=None, drawPointsFunc=None, writer=None):
	"""Return .glif data for a glyph as a UTF-8 encoded string.
	The 'glyphObject' argument can be any kind of object (even None);
	the writeGlyphToString() method will attempt to get the following
	attributes from it:
		"width"     the advance with of the glyph
		"unicodes"  a list of unicode values for this glyph
		"note"      a string
		"lib"       a dictionary containing custom data

	All attributes are optional: if 'glyphObject' doesn't
	have the attribute, it will simply be skipped.

	To write outline data to the .glif file, writeGlyphToString() needs
	a function (any callable object actually) that will take one
	argument: an object that conforms to the PointPen protocol.
	The function will be called by writeGlyphToString(); it has to call the
	proper PointPen methods to transfer the outline to the .glif file.
	"""
	if writer is None:
		try:
			# try the newer location first
			from fontTools.misc.xmlWriter import XMLWriter
		except ImportError:
			from xmlWriter import XMLWriter
		aFile = StringIO()
		writer = XMLWriter(aFile, encoding="UTF-8")
	else:
		aFile = None
	writer.begintag("glyph", [("name", glyphName), ("format", "1")])
	writer.newline()

	width = getattr(glyphObject, "width", None)
	if width is not None:
		if not isinstance(width, (int, float)):
			raise GlifLibError, "width attribute must be int or float"
		writer.simpletag("advance", width=repr(width))
		writer.newline()

	unicodes = getattr(glyphObject, "unicodes", None)
	if unicodes:
		if isinstance(unicodes, int):
			unicodes = [unicodes]
		for code in unicodes:
			if not isinstance(code, int):
				raise GlifLibError, "unicode values must be int"
			hexCode = hex(code)[2:].upper()
			if len(hexCode) < 4:
				hexCode = "0" * (4 - len(hexCode)) + hexCode
			writer.simpletag("unicode", hex=hexCode)
			writer.newline()

	note = getattr(glyphObject, "note", None)
	if note is not None:
		if not isinstance(note, (str, unicode)):
			raise GlifLibError, "note attribute must be str or unicode"
		note = note.encode('utf-8')
		writer.begintag("note")
		writer.newline()
		for line in note.splitlines():
			writer.write(line.strip())
			writer.newline()
		writer.endtag("note")
		writer.newline()

	if drawPointsFunc is not None:
		writer.begintag("outline")
		writer.newline()
		pen = GLIFPointPen(writer)
		drawPointsFunc(pen)
		writer.endtag("outline")
		writer.newline()

	lib = getattr(glyphObject, "lib", None)
	if lib:
		from robofab.plistlib import PlistWriter
		if not isinstance(lib, dict):
			lib = dict(lib)
		writer.begintag("lib")
		writer.newline()
		plistWriter = PlistWriter(writer.file, indentLevel=writer.indentlevel,
				indent=writer.indentwhite, writeHeader=False)
		plistWriter.writeValue(lib)
		writer.endtag("lib")
		writer.newline()

	writer.endtag("glyph")
	writer.newline()
	if aFile is not None:
		return aFile.getvalue()
	else:
		return None
示例#37
0
	def test_simpletag(self):
		writer = XMLWriter(BytesIO())
		writer.simpletag("tag", a="1", b="2")
		self.assertEqual(HEADER + b'<tag a="1" b="2"/>', writer.file.getvalue())
示例#38
0
 def __init__(self):
     self._file = StringIO()
     self._writer = XMLWriter(self._file, encoding="utf-8")
示例#39
0
	def test_writecdata(self):
		writer = XMLWriter(BytesIO())
		writer.writecdata("foo&bar")
		self.assertEqual(HEADER + b"<![CDATA[foo&bar]]>", writer.file.getvalue())
示例#40
0
	def test_write(self):
		writer = XMLWriter(BytesIO())
		writer.write("foo&bar")
		self.assertEqual(HEADER + b"foo&amp;bar", writer.file.getvalue())
示例#41
0
	def test_comment_multiline(self):
		writer = XMLWriter(BytesIO())
		writer.comment("Hello world\nHow are you?")
		self.assertEqual(HEADER + b"<!-- Hello world" + linesep + b"     How are you? -->",
				 writer.file.getvalue())
示例#42
0
	def test_comment_escaped(self):
		writer = XMLWriter(BytesIO())
		writer.comment("This&that are <comments>")
		self.assertEqual(HEADER + b"<!-- This&amp;that are &lt;comments&gt; -->", writer.file.getvalue())
示例#43
0
	def test_carriage_return_escaped(self):
		writer = XMLWriter(BytesIO())
		writer.write("two lines\r\nseparated by Windows line endings")
		self.assertEqual(
			HEADER + b'two lines&#13;\nseparated by Windows line endings',
			writer.file.getvalue())
示例#44
0
class Logger(object):

    def __init__(self):
        self._file = StringIO()
        self._writer = XMLWriter(self._file, encoding="utf-8")

    def __del__(self):
        self._writer = None
        self._file.close()

    def logStart(self):
        self._writer.begintag("xml")

    def logEnd(self):
        self._writer.endtag("xml")

    def logMainSettings(self, glyphNames, script, langSys):
        self._writer.begintag("initialSettings")
        self._writer.newline()
        self._writer.simpletag("string", value=" ".join(glyphNames))
        self._writer.newline()
        self._writer.simpletag("script", value=script)
        self._writer.newline()
        self._writer.simpletag("langSys", value=langSys)
        self._writer.newline()
        self._writer.endtag("initialSettings")
        self._writer.newline()

    def logTableStart(self, table):
        name = table.__class__.__name__
        self._writer.begintag("table", name=name)
        self._writer.newline()
        self.logTableFeatureStates(table)

    def logTableEnd(self):
        self._writer.endtag("table")

    def logTableFeatureStates(self, table):
        self._writer.begintag("featureStates")
        self._writer.newline()
        for tag in sorted(table.getFeatureList()):
            state = table.getFeatureState(tag)
            self._writer.simpletag("feature", name=tag, state=int(state))
            self._writer.newline()
        self._writer.endtag("featureStates")
        self._writer.newline()

    def logApplicableLookups(self, table, lookups):
        self._writer.begintag("applicableLookups")
        self._writer.newline()
        if lookups:
            order = []
            last = None
            for tag, lookup in lookups:
                if tag != last:
                    if order:
                        self._logLookupList(last, order)
                    order = []
                    last = tag
                index = table.LookupList.Lookup.index(lookup)
                order.append(index)
            self._logLookupList(last, order)
        self._writer.endtag("applicableLookups")
        self._writer.newline()

    def _logLookupList(self, tag, lookups):
        lookups = " ".join([str(i) for i in lookups])
        self._writer.simpletag("lookups", feature=tag, indices=lookups)
        self._writer.newline()

    def logProcessingStart(self):
        self._writer.begintag("processing")
        self._writer.newline()

    def logProcessingEnd(self):
        self._writer.endtag("processing")
        self._writer.newline()

    def logLookupStart(self, table, tag, lookup):
        index = table.LookupList.Lookup.index(lookup)
        self._writer.begintag("lookup", feature=tag, index=index)
        self._writer.newline()

    def logLookupEnd(self):
        self._writer.endtag("lookup")
        self._writer.newline()

    def logSubTableStart(self, lookup, subtable):
        index = lookup.SubTable.index(subtable)
        lookupType = subtable.__class__.__name__
        self._writer.begintag("subTable", index=index, type=lookupType)
        self._writer.newline()

    def logSubTableEnd(self):
        self._writer.endtag("subTable")
        self._writer.newline()

    def logGlyphRecords(self, glyphRecords):
        for r in glyphRecords:
            self._writer.simpletag("glyphRecord", name=r.glyphName,
                xPlacement=r.xPlacement, yPlacement=r.yPlacement,
                xAdvance=r.xAdvance, yAdvance=r.yAdvance)
            self._writer.newline()

    def logInput(self, processed, unprocessed):
        self._writer.begintag("input")
        self._writer.newline()
        self._writer.begintag("processed")
        self._writer.newline()
        self.logGlyphRecords(processed)
        self._writer.endtag("processed")
        self._writer.newline()
        self._writer.begintag("unprocessed")
        self._writer.newline()
        self.logGlyphRecords(unprocessed)
        self._writer.endtag("unprocessed")
        self._writer.newline()
        self._writer.endtag("input")
        self._writer.newline()

    def logOutput(self, processed, unprocessed):
        self._writer.begintag("output")
        self._writer.newline()
        self._writer.begintag("processed")
        self._writer.newline()
        self.logGlyphRecords(processed)
        self._writer.endtag("processed")
        self._writer.newline()
        self._writer.begintag("unprocessed")
        self._writer.newline()
        self.logGlyphRecords(unprocessed)
        self._writer.endtag("unprocessed")
        self._writer.newline()
        self._writer.endtag("output")
        self._writer.newline()

    def logResults(self, processed):
        self._writer.begintag("results")
        self._writer.newline()
        self.logGlyphRecords(processed)
        self._writer.endtag("results")
        self._writer.newline()

    def getText(self):
        return self._file.getvalue()
class WoffMetaDataWriter(object):

    def __init__(self, metaDataPath, fontPath):
        self.writer = XMLWriter(metaDataPath, encoding="UTF-8")
        self.font = Font(fontPath)

        self.beginMetaData()

        self.uniqueId()
        self.vendor()
        self.credits()
        self.description()
        self.license()
        self.copyright()
        self.trademark()
        self.endMetaData()

        self.writer.close()

    def beginMetaData(self):
        self.writer.begintag("metadata")
        self.writer.newline()

    def uniqueId(self):
        url = self.font.info.get("openTypeNameManufacturerURL")
        if not url:
            return
        reverseUrl = reverseDomain(url)
        name = "%s%s" % (self.font.info.familyName, self.font.info.styleName)
        name = name.replace(" ", "")
        self.writer.simpletag("uniqueid", id="%s.%s" % (reverseUrl, name))
        self.writer.newline()

    def vendor(self):
        name = self.font.info.get("openTypeNameManufacturer")
        url = self.font.info.get("openTypeNameManufacturerURL")
        if not name or not url:
            return
        self.writer.simpletag("vendor", name=name, url=url)
        self.writer.newline()

    def credits(self):
        manufacturerName = self.font.info.get("openTypeNameManufacturer")
        manufacturerUrl = self.font.info.get("openTypeNameManufacturerURL")

        designerName = self.font.info.get("openTypeNameDesigner")
        designerUrl = self.font.info.get("openTypeNameDesignerURL")

        if not manufacturerName and not manufacturerUrl and not designerName and not designerUrl:
            return

        self.writer.begintag("credits")
        self.writer.newline()

        if manufacturerName and manufacturerUrl:
            manufacturerName = manufacturerName.encode("utf-8")
            self.writer.simpletag("credit", name=manufacturerName, url=manufacturerUrl, role="Foundry")
            self.writer.newline()
        if designerName and designerUrl:
            designerName = designerName.encode("utf-8")
            self.writer.simpletag("credit", name=designerName, url=designerUrl, role="Designer")
            self.writer.newline()

        self.writer.endtag("credits")
        self.writer.newline()

    def _addData(self, tag, infoAttr, extra=dict()):
        data = self.font.info.get(infoAttr)
        if not data:
            return
        data = data.encode("utf-8")
        self.writer.begintag(tag, **extra)
        self.writer.newline()
        self.writer.begintag("text", lang="en")
        self.writer.newline()
        self.writer.write(data)
        self.writer.endtag("text")
        self.writer.newline()
        self.writer.endtag(tag)
        self.writer.newline()

    def description(self):
        self._addData("description", "openTypeNameDescription")

    def license(self):
        extra = dict()
        licenseUrl = self.font.info.get("openTypeNameLicenseURL")
        if licenseUrl:
            extra["url"] = licenseUrl
        self._addData("license", "openTypeNameLicense", extra)

    def copyright(self):
        self._addData("copyright", "copyright")

    def trademark(self):
        self._addData("trademark", "trademark")

    def endMetaData(self):
        self.writer.endtag("metadata")
        self.writer.newline()
示例#46
0
    def check_mti_file(self, name, tableTag=None):

        xml_expected_path = self.getpath("%s.ttx" % name + ('.'+tableTag if tableTag is not None else ''))
        with open(xml_expected_path, 'rt', encoding="utf-8") as xml_expected_file:
            xml_expected = xml_expected_file.read()

        font = self.create_font()

        with open(self.getpath("%s.txt" % name), 'rt', encoding="utf-8") as f:
            table = mtiLib.build(f, font, tableTag=tableTag)

        if tableTag is not None:
            self.assertEqual(tableTag, table.tableTag)
        tableTag = table.tableTag

        # Make sure it compiles.
        blob = table.compile(font)

        # Make sure it decompiles.
        decompiled = table.__class__()
        decompiled.decompile(blob, font)

        # XML from built object.
        writer = XMLWriter(StringIO(), newlinestr='\n')
        writer.begintag(tableTag); writer.newline()
        table.toXML(writer, font)
        writer.endtag(tableTag); writer.newline()
        xml_built = writer.file.getvalue()

        # XML from decompiled object.
        writer = XMLWriter(StringIO(), newlinestr='\n')
        writer.begintag(tableTag); writer.newline()
        decompiled.toXML(writer, font)
        writer.endtag(tableTag); writer.newline()
        xml_binary = writer.file.getvalue()

        self.expect_ttx(xml_binary,   xml_built, fromfile='decompiled',      tofile='built')
        self.expect_ttx(xml_expected, xml_built, fromfile=xml_expected_path, tofile='built')

        from fontTools.misc import xmlReader
        f = StringIO()
        f.write(xml_expected)
        f.seek(0)
        font2 = TTFont()
        font2.setGlyphOrder(font.getGlyphOrder())
        reader = xmlReader.XMLReader(f, font2)
        reader.read(rootless=True)

        # XML from object read from XML.
        writer = XMLWriter(StringIO(), newlinestr='\n')
        writer.begintag(tableTag); writer.newline()
        font2[tableTag].toXML(writer, font)
        writer.endtag(tableTag); writer.newline()
        xml_fromxml = writer.file.getvalue()

        self.expect_ttx(xml_expected, xml_fromxml, fromfile=xml_expected_path, tofile='fromxml')
示例#47
0
	def test_indent_dedent(self):
		writer = XMLWriter(BytesIO())
		writer.write("foo")
		writer.newline()
		writer.indent()
		writer.write("bar")
		writer.newline()
		writer.dedent()
		writer.write("baz")
		self.assertEqual(HEADER + bytesjoin(["foo", "  bar", "baz"], linesep),
				 writer.file.getvalue())
示例#48
0
	def test_begintag_endtag(self):
		writer = XMLWriter(BytesIO())
		writer.begintag("tag", attr="value")
		writer.write("content")
		writer.endtag("tag")
		self.assertEqual(HEADER + b'<tag attr="value">content</tag>', writer.file.getvalue())