{"action":"created","comment":{"url":"https://api.github.com/repos/googlei18n/cu2qu/pulls/comments/58526530","id":58526530,"diff_hunk":"@@ -0,0 +1,198 @@\n+# Copyright 2015 Google Inc. All Rights Reserved.\n+#\n+# Licensed under the Apache License, Version 2.0 (the \"License\");\n+# you may not use this file except in compliance with the License.\n+# You may obtain a copy of the License at\n+#\n+#     http://www.apache.org/licenses/LICENSE-2.0\n+#\n+# Unless required by applicable law or agreed to in writing, software\n+# distributed under the License is distributed on an \"AS IS\" BASIS,\n+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+# See the License for the specific language governing permissions and\n+# limitations under the License.\n+\n+\n+\"\"\"Converts cubic bezier curves to quadratic splines.\n+\n+Conversion is performed such that the quadratic splines keep the same end-curve\n+tangents as the original cubics. The approach is iterative, increasing the\n+number of segments for a spline until the error gets below a bound.\n+\n+Respective curves from multiple fonts will be converted at once to ensure that\n+the resulting splines are interpolation-compatible.\n+\"\"\"\n+\n+\n+from __future__ import print_function, division, absolute_import\n+\n+from fontTools.pens.basePen import AbstractPen\n+from cu2qu import curve_to_quadratic, curves_to_quadratic\n+\n+__all__ = ['fonts_to_quadratic', 'font_to_quadratic']\n+\n+DEFAULT_MAX_ERR = 0.0025\n+\n+\n+_zip = zip\n+def zip(*args):\n+    \"\"\"Ensure each argument to zip has the same length.\"\"\"\n+\n+    if len(set(len(a) for a in args)) != 1:\n+        msg = 'Args to zip in cu2qu should have equal lengths: '\n+        raise ValueError(msg + ' '.join(str(a) for a in args))\n+    return _zip(*args)\n+\n+\n+class GetSegmentsPen(AbstractPen):\n+    \"\"\"Pen to collect segments into lists of points for conversion.\n+\n+    Curves always include their initial on-curve point, so some points are\n+    duplicated between segments.\n+    \"\"\"\n+\n+    def __init__(self):\n+        self._last_pt = None\n+        self.segments = []\n+\n+    def _add_segment(self, tag, *args):\n+        if tag in ['move', 'line', 'qcurve', 'curve']:\n+            self._last_pt = args[-1]\n+        self.segments.append((tag, args))\n+\n+    def moveTo(self, pt):\n+        self._add_segment('move', pt)\n+\n+    def lineTo(self, pt):\n+        self._add_segment('line', pt)\n+\n+    def qCurveTo(self, *points):\n+        self._add_segment('qcurve', self._last_pt, *points)\n+\n+    def curveTo(self, *points):\n+        self._add_segment('curve', self._last_pt, *points)\n+\n+    def closePath(self):\n+        self._add_segment('close')\n+\n+    def endPath(self):\n+        self._add_segment('end')\n+\n+    def addComponent(self, glyphName, transformation):\n+        self._add_segment('component', glyphName, transformation)\n+\n+\n+def _get_segments(glyph):\n+    \"\"\"Get a glyph's segments as extracted by GetSegmentsPen.\"\"\"\n+\n+    pen = GetSegmentsPen()\n+    glyph.draw(pen)\n+    return pen.segments\n+\n+\n+def _set_segments(glyph, segments):\n+    \"\"\"Draw segments as extracted by GetSegmentsPen back to a glyph.\"\"\"\n+\n+    glyph.clearContours()","path":"Lib/cu2qu/ufo.py","position":96,"original_position":96,"commit_id":"983307d9cd005cd481f7a1664dc3b32e58950fd5","original_commit_id":"983307d9cd005cd481f7a1664dc3b32e58950fd5","user":{"login":"anthrotype","id":6939968,"avatar_url":"https://avatars.githubusercontent.com/u/6939968?v=3","gravatar_id":"","url":"https://api.github.com/users/anthrotype","html_url":"https://github.com/anthrotype","followers_url":"https://api.github.com/users/anthrotype/followers","following_url":"https://api.github.com/users/anthrotype/following{/other_user}","gists_url":"https://api.github.com/users/anthrotype/gists{/gist_id}","starred_url":"https://api.github.com/users/anthrotype/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/anthrotype/subscriptions","organizations_url":"https://api.github.com/users/anthrotype/orgs","repos_url":"https://api.github.com/users/anthrotype/repos","events_url":"https://api.github.com/users/anthrotype/events{/privacy}","received_events_url":"https://api.github.com/users/anthrotype/received_events","type":"User","site_admin":false},"body":"There's also another issue here which is somewhat related to this.\r\nWhen working from within a UFO2 environment (robofab/master or defcon/master), the current `cu2qu.ufo` writes the anchors twice.\r\nThis is because in UFO2-based libraries the pens also \"draw\" anchors (i.e.interpreted as single points with a \"move\" type and a \"name\" attribute) as well as contours and components.\r\nhttps://github.com/typesupply/defcon/blob/ufo3/Lib/defcon/objects/glyph.py#L363-L373\r\nhttps://github.com/typesupply/defcon/blob/master/Lib/defcon/objects/glyph.py#L304-L313\r\n\r\nWhen using defcon/ufo3, anchors are separate objects whose data is not passed along by the `drawPoints`:\r\nhttps://github.com/typesupply/defcon/blob/ufo3/Lib/defcon/objects/glyph.py#L363-L373\r\n\r\nOne fix would be to call `glyph.clearAnchors()` but only when drawing on a UFO2-kind of glyph (but not when the glyph is UFO3, since in this case the pens would not re-draw the anchors).\r\n\r\nThese are some of the problems that I got stuck into when I was trying to find a general way to use pens to convert glyphs from different environments. I believe that cu2qu, at least in its most general API, should only take care of creating new glyph-like objects (exporting a minimal `draw` and `drawPoints` API) which then the caller will use to draw on the specific \"canvas\" it is working with. \r\n\r\n","created_at":"2016-04-05T12:11:02Z","updated_at":"2016-04-05T12:11:02Z","html_url":"https://github.com/googlei18n/cu2qu/pull/21#discussion_r58526530","pull_request_url":"https://api.github.com/repos/googlei18n/cu2qu/pulls/21","_links":{"self":{"href":"https://api.github.com/repos/googlei18n/cu2qu/pulls/comments/58526530"},"html":{"href":"https://github.com/googlei18n/cu2qu/pull/21#discussion_r58526530"},"pull_request":{"href":"https://api.github.com/repos/googlei18n/cu2qu/pulls/21"}}},"pull_request":{"url":"https://api.github.com/repos/googlei18n/cu2qu/pulls/21","id":65222676,"html_url":"https://github.com/googlei18n/cu2qu/pull/21","diff_url":"https://github.com/googlei18n/cu2qu/pull/21.diff","patch_url":"https://github.com/googlei18n/cu2qu/pull/21.patch","issue_url":"https://api.github.com/repos/googlei18n/cu2qu/issues/21","number":21,"state":"closed","locked":false,"title":"Defcon compatibility","user":{"login":"jamesgk","id":988758,"avatar_url":"https://avatars.githubusercontent.com/u/988758?v=3","gravatar_id":"","url":"https://api.github.com/users/jamesgk","html_url":"https://github.com/jamesgk","followers_url":"https://api.github.com/users/jamesgk/followers","following_url":"https://api.github.com/users/jamesgk/following{/other_user}","gists_url":"https://api.github.com/users/jamesgk/gists{/gist_id}","starred_url":"https://api.github.com/users/jamesgk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jamesgk/subscriptions","organizations_url":"https://api.github.com/users/jamesgk/orgs","repos_url":"https://api.github.com/users/jamesgk/repos","events_url":"https://api.github.com/users/jamesgk/events{/privacy}","received_events_url":"https://api.github.com/users/jamesgk/received_events","type":"User","site_admin":false},"body":"cc @behdad","created_at":"2016-04-04T22:21:07Z","updated_at":"2016-04-05T12:11:02Z","closed_at":"2016-04-05T00:49:03Z","merged_at":"2016-04-05T00:49:03Z","merge_commit_sha":"f6853d0e48e67535fd3377db46f21b88eaa025b7","assignee":{"login":"dougfelt","id":9939212,"avatar_url":"https://avatars.githubusercontent.com/u/9939212?v=3","gravatar_id":"","url":"https://api.github.com/users/dougfelt","html_url":"https://github.com/dougfelt","followers_url":"https://api.github.com/users/dougfelt/followers","following_url":"https://api.github.com/users/dougfelt/following{/other_user}","gists_url":"https://api.github.com/users/dougfelt/gists{/gist_id}","starred_url":"https://api.github.com/users/dougfelt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dougfelt/subscriptions","organizations_url":"https://api.github.com/users/dougfelt/orgs","repos_url":"https://api.github.com/users/dougfelt/repos","events_url":"https://api.github.com/users/dougfelt/events{/privacy}","received_events_url":"https://api.github.com/users/dougfelt/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/googlei18n/cu2qu/pulls/21/commits","review_comments_url":"https://api.github.com/repos/googlei18n/cu2qu/pulls/21/comments","review_comment_url":"https://api.github.com/repos/googlei18n/cu2qu/pulls/comments{/number}","comments_url":"https://api.github.com/repos/googlei18n/cu2qu/issues/21/comments","statuses_url":"https://api.github.com/repos/googlei18n/cu2qu/statuses/983307d9cd005cd481f7a1664dc3b32e58950fd5","head":{"label":"googlei18n:defcon","ref":"defcon","sha":"983307d9cd005cd481f7a1664dc3b32e58950fd5","user":{"login":"googlei18n","id":5503833,"avatar_url":"https://avatars.githubusercontent.com/u/5503833?v=3","gravatar_id":"","url":"https://api.github.com/users/googlei18n","html_url":"https://github.com/googlei18n","followers_url":"https://api.github.com/users/googlei18n/followers","following_url":"https://api.github.com/users/googlei18n/following{/other_user}","gists_url":"https://api.github.com/users/googlei18n/gists{/gist_id}","starred_url":"https://api.github.com/users/googlei18n/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/googlei18n/subscriptions","organizations_url":"https://api.github.com/users/googlei18n/orgs","repos_url":"https://api.github.com/users/googlei18n/repos","events_url":"https://api.github.com/users/googlei18n/events{/privacy}","received_events_url":"https://api.github.com/users/googlei18n/received_events","type":"Organization","site_admin":false},"repo":{"id":45646922,"name":"cu2qu","full_name":"googlei18n/cu2qu","owner":{"login":"googlei18n","id":5503833,"avatar_url":"https://avatars.githubusercontent.com/u/5503833?v=3","gravatar_id":"","url":"https://api.github.com/users/googlei18n","html_url":"https://github.com/googlei18n","followers_url":"https://api.github.com/users/googlei18n/followers","following_url":"https://api.github.com/users/googlei18n/following{/other_user}","gists_url":"https://api.github.com/users/googlei18n/gists{/gist_id}","starred_url":"https://api.github.com/users/googlei18n/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/googlei18n/subscriptions","organizations_url":"https://api.github.com/users/googlei18n/orgs","repos_url":"https://api.github.com/users/googlei18n/repos","events_url":"https://api.github.com/users/googlei18n/events{/privacy}","received_events_url":"https://api.github.com/users/googlei18n/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/googlei18n/cu2qu","description":"","fork":false,"url":"https://api.github.com/repos/googlei18n/cu2qu","forks_url":"https://api.github.com/repos/googlei18n/cu2qu/forks","keys_url":"https://api.github.com/repos/googlei18n/cu2qu/keys{/key_id}","collaborators_url":"https://api.github.com/repos/googlei18n/cu2qu/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/googlei18n/cu2qu/teams","hooks_url":"https://api.github.com/repos/googlei18n/cu2qu/hooks","issue_events_url":"https://api.github.com/repos/googlei18n/cu2qu/issues/events{/number}","events_url":"https://api.github.com/repos/googlei18n/cu2qu/events","assignees_url":"https://api.github.com/repos/googlei18n/cu2qu/assignees{/user}","branches_url":"https://api.github.com/repos/googlei18n/cu2qu/branches{/branch}","tags_url":"https://api.github.com/repos/googlei18n/cu2qu/tags","blobs_url":"https://api.github.com/repos/googlei18n/cu2qu/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/googlei18n/cu2qu/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/googlei18n/cu2qu/git/refs{/sha}","trees_url":"https://api.github.com/repos/googlei18n/cu2qu/git/trees{/sha}","statuses_url":"https://api.github.com/repos/googlei18n/cu2qu/statuses/{sha}","languages_url":"https://api.github.com/repos/googlei18n/cu2qu/languages","stargazers_url":"https://api.github.com/repos/googlei18n/cu2qu/stargazers","contributors_url":"https://api.github.com/repos/googlei18n/cu2qu/contributors","subscribers_url":"https://api.github.com/repos/googlei18n/cu2qu/subscribers","subscription_url":"https://api.github.com/repos/googlei18n/cu2qu/subscription","commits_url":"https://api.github.com/repos/googlei18n/cu2qu/commits{/sha}","git_commits_url":"https://api.github.com/repos/googlei18n/cu2qu/git/commits{/sha}","comments_url":"https://api.github.com/repos/googlei18n/cu2qu/comments{/number}","issue_comment_url":"https://api.github.com/repos/googlei18n/cu2qu/issues/comments{/number}","contents_url":"https://api.github.com/repos/googlei18n/cu2qu/contents/{+path}","compare_url":"https://api.github.com/repos/googlei18n/cu2qu/compare/{base}...{head}","merges_url":"https://api.github.com/repos/googlei18n/cu2qu/merges","archive_url":"https://api.github.com/repos/googlei18n/cu2qu/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/googlei18n/cu2qu/downloads","issues_url":"https://api.github.com/repos/googlei18n/cu2qu/issues{/number}","pulls_url":"https://api.github.com/repos/googlei18n/cu2qu/pulls{/number}","milestones_url":"https://api.github.com/repos/googlei18n/cu2qu/milestones{/number}","notifications_url":"https://api.github.com/repos/googlei18n/cu2qu/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/googlei18n/cu2qu/labels{/name}","releases_url":"https://api.github.com/repos/googlei18n/cu2qu/releases{/id}","deployments_url":"https://api.github.com/repos/googlei18n/cu2qu/deployments","created_at":"2015-11-05T23:40:44Z","updated_at":"2016-02-17T08:43:24Z","pushed_at":"2016-04-05T00:49:54Z","git_url":"git://github.com/googlei18n/cu2qu.git","ssh_url":"git@github.com:googlei18n/cu2qu.git","clone_url":"https://github.com/googlei18n/cu2qu.git","svn_url":"https://github.com/googlei18n/cu2qu","homepage":null,"size":67,"stargazers_count":11,"watchers_count":11,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":9,"mirror_url":null,"open_issues_count":6,"forks":9,"open_issues":6,"watchers":11,"default_branch":"master"}},"base":{"label":"googlei18n:master","ref":"master","sha":"7014fc7c3a8532cd585a6640d65a1a3bbd7fdc82","user":{"login":"googlei18n","id":5503833,"avatar_url":"https://avatars.githubusercontent.com/u/5503833?v=3","gravatar_id":"","url":"https://api.github.com/users/googlei18n","html_url":"https://github.com/googlei18n","followers_url":"https://api.github.com/users/googlei18n/followers","following_url":"https://api.github.com/users/googlei18n/following{/other_user}","gists_url":"https://api.github.com/users/googlei18n/gists{/gist_id}","starred_url":"https://api.github.com/users/googlei18n/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/googlei18n/subscriptions","organizations_url":"https://api.github.com/users/googlei18n/orgs","repos_url":"https://api.github.com/users/googlei18n/repos","events_url":"https://api.github.com/users/googlei18n/events{/privacy}","received_events_url":"https://api.github.com/users/googlei18n/received_events","type":"Organization","site_admin":false},"repo":{"id":45646922,"name":"cu2qu","full_name":"googlei18n/cu2qu","owner":{"login":"googlei18n","id":5503833,"avatar_url":"https://avatars.githubusercontent.com/u/5503833?v=3","gravatar_id":"","url":"https://api.github.com/users/googlei18n","html_url":"https://github.com/googlei18n","followers_url":"https://api.github.com/users/googlei18n/followers","following_url":"https://api.github.com/users/googlei18n/following{/other_user}","gists_url":"https://api.github.com/users/googlei18n/gists{/gist_id}","starred_url":"https://api.github.com/users/googlei18n/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/googlei18n/subscriptions","organizations_url":"https://api.github.com/users/googlei18n/orgs","repos_url":"https://api.github.com/users/googlei18n/repos","events_url":"https://api.github.com/users/googlei18n/events{/privacy}","received_events_url":"https://api.github.com/users/googlei18n/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/googlei18n/cu2qu","description":"","fork":false,"url":"https://api.github.com/repos/googlei18n/cu2qu","forks_url":"https://api.github.com/repos/googlei18n/cu2qu/forks","keys_url":"https://api.github.com/repos/googlei18n/cu2qu/keys{/key_id}","collaborators_url":"https://api.github.com/repos/googlei18n/cu2qu/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/googlei18n/cu2qu/teams","hooks_url":"https://api.github.com/repos/googlei18n/cu2qu/hooks","issue_events_url":"https://api.github.com/repos/googlei18n/cu2qu/issues/events{/number}","events_url":"https://api.github.com/repos/googlei18n/cu2qu/events","assignees_url":"https://api.github.com/repos/googlei18n/cu2qu/assignees{/user}","branches_url":"https://api.github.com/repos/googlei18n/cu2qu/branches{/branch}","tags_url":"https://api.github.com/repos/googlei18n/cu2qu/tags","blobs_url":"https://api.github.com/repos/googlei18n/cu2qu/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/googlei18n/cu2qu/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/googlei18n/cu2qu/git/refs{/sha}","trees_url":"https://api.github.com/repos/googlei18n/cu2qu/git/trees{/sha}","statuses_url":"https://api.github.com/repos/googlei18n/cu2qu/statuses/{sha}","languages_url":"https://api.github.com/repos/googlei18n/cu2qu/languages","stargazers_url":"https://api.github.com/repos/googlei18n/cu2qu/stargazers","contributors_url":"https://api.github.com/repos/googlei18n/cu2qu/contributors","subscribers_url":"https://api.github.com/repos/googlei18n/cu2qu/subscribers","subscription_url":"https://api.github.com/repos/googlei18n/cu2qu/subscription","commits_url":"https://api.github.com/repos/googlei18n/cu2qu/commits{/sha}","git_commits_url":"https://api.github.com/repos/googlei18n/cu2qu/git/commits{/sha}","comments_url":"https://api.github.com/repos/googlei18n/cu2qu/comments{/number}","issue_comment_url":"https://api.github.com/repos/googlei18n/cu2qu/issues/comments{/number}","contents_url":"https://api.github.com/repos/googlei18n/cu2qu/contents/{+path}","compare_url":"https://api.github.com/repos/googlei18n/cu2qu/compare/{base}...{head}","merges_url":"https://api.github.com/repos/googlei18n/cu2qu/merges","archive_url":"https://api.github.com/repos/googlei18n/cu2qu/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/googlei18n/cu2qu/downloads","issues_url":"https://api.github.com/repos/googlei18n/cu2qu/issues{/number}","pulls_url":"https://api.github.com/repos/googlei18n/cu2qu/pulls{/number}","milestones_url":"https://api.github.com/repos/googlei18n/cu2qu/milestones{/number}","notifications_url":"https://api.github.com/repos/googlei18n/cu2qu/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/googlei18n/cu2qu/labels{/name}","releases_url":"https://api.github.com/repos/googlei18n/cu2qu/releases{/id}","deployments_url":"https://api.github.com/repos/googlei18n/cu2qu/deployments","created_at":"2015-11-05T23:40:44Z","updated_at":"2016-02-17T08:43:24Z","pushed_at":"2016-04-05T00:49:54Z","git_url":"git://github.com/googlei18n/cu2qu.git","ssh_url":"git@github.com:googlei18n/cu2qu.git","clone_url":"https://github.com/googlei18n/cu2qu.git","svn_url":"https://github.com/googlei18n/cu2qu","homepage":null,"size":67,"stargazers_count":11,"watchers_count":11,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":9,"mirror_url":null,"open_issues_count":6,"forks":9,"open_issues":6,"watchers":11,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/googlei18n/cu2qu/pulls/21"},"html":{"href":"https://github.com/googlei18n/cu2qu/pull/21"},"issue":{"href":"https://api.github.com/repos/googlei18n/cu2qu/issues/21"},"comments":{"href":"https://api.github.com/repos/googlei18n/cu2qu/issues/21/comments"},"review_comments":{"href":"https://api.github.com/repos/googlei18n/cu2qu/pulls/21/comments"},"review_comment":{"href":"https://api.github.com/repos/googlei18n/cu2qu/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/googlei18n/cu2qu/pulls/21/commits"},"statuses":{"href":"https://api.github.com/repos/googlei18n/cu2qu/statuses/983307d9cd005cd481f7a1664dc3b32e58950fd5"}}}}