Font resources?

Ah. Thanks for mentioning it’s flipped. I now remember that when I printed a character I know (the number “4”) it was upside down. I think the KiCAD X/Y directions (Y being opposite sign to many other programs) caught me. I’ll fix. Thanks!

無 problem…

Is this better? I went ahead and developed the framework for full 2d transformations using a matrix. This will make it easier to place, rotate, resize, shear and any other transformations in 2d space using 3x3 matrix transformation.

Edit: I work on so many different things that I find myself relearning over and over (and over). I’m pretty sure that with 3x3 matrix transformation in 2d space, you can represent any Affine Transformation “Examples of affine transformations include translation, scaling, homothety, similarity, reflection, rotation, shear mapping, and compositions of them in any combination and sequence.” (Wikipedia)

Yes, that mu character is correct now.

This is of course impossible (in any simple way) because the TTF fonts are by definition outlines. It doesn’t know about strokes. For certain fonts it would be possible to programmatically calculate the strokes, but I don’t think it would work for nearly all fonts.

I’m interested in seeing how you convert the glyphs to polygons and how they are rendered in different layers (especially copper, silk and mask). Drawing the outlines with segments isn’t very useful for most use cases.

In my recent work on KiCommand to add (what I call) geoms, I added support for all DRAWSEGMENT shapes, including Bezier curves. It was a short (ish) exercise to convert SVG “d” path support and convert SVG d path commands into geoms. KiCAD implements (only?) cubic Bezier curves, so I added code to convert the SVG quadratic Bezier curve into cubic. I had to totally rework my existing fromsvg code to handle a more general input format. A couple of years ago, I posted the results of KiCommand’s ability to add text similar to your image.

As far as translating the outlines to polygons, this will be a little harder because KiCAD zones only support straight line edges. However, it’s technically possible (mathematically) to convert each of the SVG d path commands (shapes) into a straight-edged outline. I’d have to work through the possible difference in polygon specification (SVG: the “inside” of the polygon is to the right). I don’t think KiCAD recognizes the difference between CW/RW and CCW/LW points. It is also the standard for GIS ShapeFile format and other GIS formats, where outer contours are CCW/LW and interior contours (holes) are CW/RW. This also makes the 2d calculations easier (I think).

In any case, I’ll see what I can do to generate polygons from SVG.

I believe this is exactly what the snippet in the stackexchange answer linked to in the bug report does. https://gis.stackexchange.com/questions/258076/how-to-generate-wkt-geometries-from-true-type-font-glyphs-python. It seems to create a simplified outline with straight edges. Internally it seems to “render” the glyph so that “pixels” will be the corners of the polygon.

Yes, indeed. That looks very promising. Is there a standard way for KiCAD plugins to require a package? I’ve written all my KiCAD code from scratch (and copy/convert/paste from a wide variety of websites) and haven’t relied on installed packages. I’m not sure if a “normal user” could navigate the installation of third-party Python packages into KiCAD’s python and plugin framework. Do you have any observations on that topic?

AFAIK there’s no way to ensure or even ask the user for installation of an external python library. I’m not terribly familiar with the situation, but probably the pip repo has all interesting packages. Pip itself is part of python.

External requirements could be part of the planned content manager. Has @qu1ck thought about this?

About non-straight polygon outlines, give your vote here: https://gitlab.com/kicad/code/kicad/-/issues/4493. (I added a comment about beziers there. Beziers would probably be relatively easy to approximate with a couple of arcs, though, and there should exist algorithms for that.)

1 Like

I would also advocate for elliptical arcs similar to SVG a and A d path commands.

Yes, support for installing dependencies is planned but likely won’t be in first iteration of plugin manager.

Here is a Polygon with holes of the PHI character on F.SilkS:
image

It was created from a ttf file using an online converter to SVG. Then I took the “d” path in the SVG glyph element and converted to “geoms” in KiCommand, then converted that to Polygon “geom” and finally used newdrawing to draw it.

"M633 1437q0 83 78 83h290q86 0 86 -78q0 -72 -88 -72h-208v-72q216 0 366 -151q159 -160 159 -389t-204 -405q-137 -118 -321 -118v-134q0 -101 -91 -101h-282q-89 0 -89 73q0 76 90 76h214v86q-217 0 -373 156q-160 160 -160 361q0 228 120 355q184 195 413 195v135z M633 1147q-132 0 -245 -88q-134 -104 -134 -293q0 -171 128 -284q102 -90 251 -90v755zM792 1147v-755q164 0 263 108q110 120 110 277q0 164 -134 282q-100 88 -239 88z"
0.02 mm fromsvg geomtopolygon F.SilkS l param newdrawing refresh

This is what the “geom” specification of PHI character looks like:

[['Polygon',
  wxPoint(12660000, -28740000),
  wxPoint(12679259, -29088394),
  wxPoint(12737037, -29395802),
  wxPoint(12833333, -29662221),
  wxPoint(12968148, -29887654),
  wxPoint(13141481, -30072098),
  wxPoint(13353333, -30215555),
  wxPoint(13603703, -30318024),
  wxPoint(13892592, -30379506),
  wxPoint(14220000, -30400000),
  wxPoint(14220000, -30400000),
  wxPoint(20020000, -30400000),
  wxPoint(20020000, -30400000),
  wxPoint(20380987, -30380740),
  wxPoint(20699505, -30322962),
  wxPoint(20975555, -30226666),
  wxPoint(21209135, -30091851),
  wxPoint(21400246, -29918518),
  wxPoint(21548888, -29706666),
  wxPoint(21655061, -29456296),
  wxPoint(21718765, -29167407),
  wxPoint(21740000, -28840000),
  wxPoint(21740000, -28840000),
  wxPoint(21718271, -28537777),
  wxPoint(21653086, -28271111),
  wxPoint(21544444, -28040000),
  wxPoint(21392345, -27844444),
  wxPoint(21196789, -27684444),
  wxPoint(20957777, -27560000),
  wxPoint(20675308, -27471111),
  wxPoint(20349382, -27417777),
  wxPoint(19980000, -27400000),
  wxPoint(19980000, -27400000),
  wxPoint(15820000, -27400000),
  wxPoint(15820000, -27400000),
  wxPoint(15820000, -25960000),
  wxPoint(15820000, -25960000),
  wxPoint(16763703, -25922716),
  wxPoint(17674814, -25810864),
  wxPoint(18553333, -25624444),
  wxPoint(19399259, -25363456),
  wxPoint(20212592, -25027901),
  wxPoint(20993333, -24617777),
  wxPoint(21741481, -24133086),
  wxPoint(22457037, -23573827),
  wxPoint(23140000, -22940000),
  wxPoint(23140000, -22940000),
  wxPoint(23807407, -22211851),
  wxPoint(24396296, -21449629),
  wxPoint(24906666, -20653332),
  wxPoint(25338518, -19822962),
  wxPoint(25691851, -18958518),
  wxPoint(25966666, -18059999),
  wxPoint(26162962, -17127407),
  wxPoint(26280740, -16160740),
  wxPoint(26320000, -15160000),
  wxPoint(26320000, -15160000),
  wxPoint(26269629, -14155308),
  wxPoint(26118518, -13176789),
  wxPoint(25866666, -12224444),
  wxPoint(25514074, -11298271),
  wxPoint(25060740, -10398271),
  wxPoint(24506666, -9524444),
  wxPoint(23851851, -8676789),
  wxPoint(23096296, -7855308),
  wxPoint(22240000, -7060000),
  wxPoint(22240000, -7060000),
  wxPoint(21619506, -6564691),
  wxPoint(20975802, -6127654),
  wxPoint(20308888, -5748888),
  wxPoint(19618765, -5428394),
  wxPoint(18905431, -5166172),
  wxPoint(18168888, -4962222),
  wxPoint(17409135, -4816543),
  wxPoint(16626172, -4729135),
  wxPoint(15820000, -4700000),
  wxPoint(15820000, -4700000),
  wxPoint(15820000, -2020000),
  wxPoint(15820000, -2020000),
  wxPoint(15797530, -1596049),
  wxPoint(15730123, -1221975),
  wxPoint(15617777, -897777),
  wxPoint(15460493, -623456),
  wxPoint(15258271, -399012),
  wxPoint(15011110, -224444),
  wxPoint(14719012, -99753),
  wxPoint(14381975, -24938),
  wxPoint(14000000, 0),
  wxPoint(14000000, 0),
  wxPoint(8360000, 0),
  wxPoint(8360000, 0),
  wxPoint(7986419, -18024),
  wxPoint(7656789, -72098),
  wxPoint(7371110, -162222),
  wxPoint(7129382, -288394),
  wxPoint(6931604, -450617),
  wxPoint(6777777, -648888),
  wxPoint(6667901, -883209),
  wxPoint(6601975, -1153580),
  wxPoint(6580000, -1460000),
  wxPoint(6580000, -1460000),
  wxPoint(6602222, -1779012),
  wxPoint(6668888, -2060493),
  wxPoint(6780000, -2304444),
  wxPoint(6935555, -2510864),
  wxPoint(7135555, -2679752),
  wxPoint(7380000, -2811111),
  wxPoint(7668888, -2904938),
  wxPoint(8002222, -2961234),
  wxPoint(8380000, -2980000),
  wxPoint(8380000, -2980000),
  wxPoint(12660000, -2980000),
  wxPoint(12660000, -2980000),
  wxPoint(12660000, -4700000),
  wxPoint(12660000, -4700000),
  wxPoint(11710617, -4738518),
  wxPoint(10791357, -4854074),
  wxPoint(9902221, -5046666),
  wxPoint(9043209, -5316296),
  wxPoint(8214320, -5662962),
  wxPoint(7415555, -6086666),
  wxPoint(6646913, -6587407),
  wxPoint(5908395, -7165185),
  wxPoint(5200000, -7820000),
  wxPoint(5200000, -7820000),
  wxPoint(4528394, -8541234),
  wxPoint(3935802, -9282715),
  wxPoint(3422221, -10044444),
  wxPoint(2987654, -10826419),
  wxPoint(2632098, -11628641),
  wxPoint(2355555, -12451111),
  wxPoint(2158024, -13293827),
  wxPoint(2039506, -14156790),
  wxPoint(2000000, -15040000),
  wxPoint(2000000, -15040000),
  wxPoint(2029629, -16028395),
  wxPoint(2118518, -16966913),
  wxPoint(2266666, -17855555),
  wxPoint(2474074, -18694320),
  wxPoint(2740740, -19483209),
  wxPoint(3066666, -20222221),
  wxPoint(3451851, -20911357),
  wxPoint(3896296, -21550617),
  wxPoint(4400000, -22140000),
  wxPoint(4400000, -22140000),
  wxPoint(5228888, -22958518),
  wxPoint(6079999, -23680740),
  wxPoint(6953333, -24306666),
  wxPoint(7848888, -24836296),
  wxPoint(8766666, -25269629),
  wxPoint(9706666, -25606666),
  wxPoint(10668888, -25847407),
  wxPoint(11653333, -25991851),
  wxPoint(12660000, -26040000),
  wxPoint(12660000, -26040000),
  wxPoint(12660000, -28740000)],
 ['Hole',
  wxPoint(12660000, -22940000),
  wxPoint(12078024, -22918271),
  wxPoint(11505432, -22853086),
  wxPoint(10942222, -22744444),
  wxPoint(10388394, -22592345),
  wxPoint(9843950, -22396789),
  wxPoint(9308888, -22157777),
  wxPoint(8783209, -21875308),
  wxPoint(8266913, -21549382),
  wxPoint(7760000, -21180000),
  wxPoint(7760000, -21180000),
  wxPoint(7197530, -20696790),
  wxPoint(6701234, -20171604),
  wxPoint(6271110, -19604444),
  wxPoint(5907160, -18995308),
  wxPoint(5609382, -18344197),
  wxPoint(5377777, -17651111),
  wxPoint(5212345, -16916049),
  wxPoint(5113086, -16139012),
  wxPoint(5080000, -15320000),
  wxPoint(5080000, -15320000),
  wxPoint(5111604, -14574320),
  wxPoint(5206419, -13857283),
  wxPoint(5364444, -13168888),
  wxPoint(5585678, -12509135),
  wxPoint(5870123, -11878024),
  wxPoint(6217777, -11275555),
  wxPoint(6628641, -10701728),
  wxPoint(7102715, -10156543),
  wxPoint(7640000, -9640000),
  wxPoint(7640000, -9640000),
  wxPoint(8104938, -9262222),
  wxPoint(8593086, -8928888),
  wxPoint(9104444, -8640000),
  wxPoint(9639012, -8395555),
  wxPoint(10196789, -8195555),
  wxPoint(10777777, -8040000),
  wxPoint(11381975, -7928888),
  wxPoint(12009382, -7862222),
  wxPoint(12660000, -7840000),
  wxPoint(12660000, -7840000),
  wxPoint(12660000, -22940000)],
 ['Hole',
  wxPoint(15840000, -22940000),
  wxPoint(15840000, -7840000),
  wxPoint(15840000, -7840000),
  wxPoint(16552839, -7866666),
  wxPoint(17233579, -7946666),
  wxPoint(17882221, -8080000),
  wxPoint(18498765, -8266666),
  wxPoint(19083209, -8506666),
  wxPoint(19635555, -8800000),
  wxPoint(20155802, -9146666),
  wxPoint(20643950, -9546666),
  wxPoint(21100000, -10000000),
  wxPoint(21100000, -10000000),
  wxPoint(21561728, -10542469),
  wxPoint(21969135, -11103209),
  wxPoint(22322221, -11682222),
  wxPoint(22620987, -12279505),
  wxPoint(22865431, -12895061),
  wxPoint(23055555, -13528888),
  wxPoint(23191357, -14180987),
  wxPoint(23272839, -14851357),
  wxPoint(23300000, -15540000),
  wxPoint(23300000, -15540000),
  wxPoint(23266913, -16257530),
  wxPoint(23167654, -16952345),
  wxPoint(23002222, -17624444),
  wxPoint(22770617, -18273826),
  wxPoint(22472839, -18900493),
  wxPoint(22108888, -19504444),
  wxPoint(21678765, -20085678),
  wxPoint(21182468, -20644197),
  wxPoint(20620000, -21180000),
  wxPoint(20620000, -21180000),
  wxPoint(20165925, -21549382),
  wxPoint(19692592, -21875308),
  wxPoint(19199999, -22157777),
  wxPoint(18688147, -22396789),
  wxPoint(18157036, -22592345),
  wxPoint(17606666, -22744444),
  wxPoint(17037036, -22853086),
  wxPoint(16448148, -22918271),
  wxPoint(15840000, -22940000)]]
1 Like

I’m having a problem moving polygons that have holes in them. When I select the polygon, the holes are also selected. That seems normal. Then when I move the polygon, the outline moves fine, but the holes stay where they are. Can anyone confirm this behavior? I’m using a nightly from earlier this year.

Both in 5.1.6 and in recent nightly zones with cutouts move together with cutouts.
Try creating zone with a cutout from gui and then inspect it’s polygon set object from api to compare what your script does wrong.

Edit: normal polygon graphic elements (not zones) don’t support holes, there is no way to add them in gui so probably that’s why moving such polygons doesn’t work properly. You should Fracture() such polgons to merge their holes into outline.

Also I think this functionality will be very useful as a standalone action plugin: pop a dialog that allows user to choose font, text, size, layer and it will generate a graphic element with that text on chosen layer and plop it on the board.

1 Like

I wrote some python code to convert TTF font glyphs into SVG. It uses the fontTools package, so it won’t work within KiCAD, but I can create a library of SVG glyphs for use within KiCAD (and maybe use them in KiCommand as well).

I use a lot of iterators in my code and try to not create “memory holes” (i.e. constant creation of temporary lists mixed with large data structures).

# Python code using FontTools to extract glyph data, then convert each glyph to SVG format.

svgddict = {}
for gn in ttf.getGlyphNames():#[0:2]:
    currentsvg = []
    print('\n',gn,end=' ')
    curve=[0.0,0.0,0.0]
    coords = ttf['glyf'][gn].getCoordinates(ttf['glyf'])
    pointiterator = map(list,(zip(*map(coords.__getitem__, (0,2)))))
    previousindex = 0
    print('totalpoints=', len(coords.__getitem__(0)),end=' ')
    for lastindex in coords.__getitem__(1):
        sliceiterator = iter(itertools.islice(pointiterator,0,lastindex-previousindex))
        print('numpoints={}'.format(lastindex-previousindex),end=' ')
        curve[0],laston = sliceiterator.__next__()
        currentsvg.append('M {} {}'.format(*curve[0]))
        for p,on in sliceiterator:
            if on:
                if laston:
                    # straight line from curve[0] to p
                    currentsvg.append('L {} {}'.format(*p))
                    curve[0] = p
                else:
                    curve[2] = p
                    # cubic bezier with points in curve 0,1,2
                    currentsvg.append('Q {} {} {} {}'.format(*curve[1],*curve[2]))
                    curve[0] = p
            else:
                if laston:
                    curve[1] = p
                else:
                    curve[2] = ((curve[1][0]+p[0])/2.0,(curve[1][1]+p[1])/2.0)
                    # cubic bezier with points in curve 0,1,2
                    currentsvg.append('Q {} {} {} {}'.format(*curve[1],*curve[2]))
                    curve[0] = curve[2]
                    curve[1] = p
            laston = on
        previousindex = lastindex
    svgddict[gn] = ' '.join(currentsvg)

Few points of caution:

  1. Font is a lot more than just bunch of glyph shapes. It’s also kerning, aliasing hints, in some cases even embedded custom executable code. While you probably don’t care about most of it, getting at least kerning right is important for any non fixed width font.
  2. Be careful with licenses. Most popular fonts are not free to distribute, not in original and not in a derived form like svg data. If you plan to bundle any font data with your plugin make sure it’s based on actually free fonts.
1 Like

That’s what I said in the bug report linked to above.

Yes, really. Licensing is another world with fonts. Often you are allowed to embed font information in text – othwise the purpose of font would be defeated. But you may not be allowed to embed the whole font file. It will be a problem when we talk about documents which should be edited later. For company names etc. it’s enough to have the name as graphics without need to edit. But in an OSHW or closed source pcb you probably want to be able to keep the project platform agnostic. It’s not if you just select a system font and translate it to pcb graphics. You have to have the font available when the project is moved or platform changed.

Thank you for this. I’m actually usually careful about licensing. I have found a lot of suitable fonts, but the spacing and kerning for automatic text layout is in the future. :slight_smile:
If someday KiCommand is bundled with KiCAD, what are the licensing requirements of the bundled project?

There a low chance to get any scripts officially bundled with KiCad. IMO the Google fonts and any font in the official Debian or some other strict linux distro should be safe.

BTW, you should defintely follow the situation in the bug report. Wayne commented that he may be working on native font support: https://gitlab.com/kicad/code/kicad/-/issues/2441#note_357250120. (A tip: add a reaction to the issue in top level, and you’ll get notified about events in the issue. Commenting there does the same.)

And here’s another milestone! I used my new program to extract “zeta” from a ttf file, then plugged it into the development version of KiCommand to use fromsvg geomtopolygon newdrawing refresh commands and got this in KiCAD:

2 Likes