Font resources?

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

More fun with fonts. Using my TTF conversion code based on fontTools and choosing a character (glyph) at random, I tried a few different rotations and translations. I think my transformation code is working (finally). You can now do any affine transforms before or between characters as desired. Right now, it’s basically manual coordinate entry for the placement, but that will change at some point.

I have identified a few fonts that might be useful. In case anyone wants to comment on these or recommend looking at others, please do.

Noto fonts from Google:

https://www.google.com/get/noto/

“Commissioned by Google, the font is licensed under the SIL Open Font License.”

osifont from hikiko mori:

“In some European countries, CAD projects must have font which conform to IS0 3O98 specification.”

Free IS0 3O98 font for CAD available under three different licenses. I chose the font under GNU LGPL licence version 3 with GPL font exception.

CamBam Stick Fonts from George Race.

Font: http://www.mrrace.com/CamBam_Fonts/

Forums: https://cambamcnc.com/forum/index.php?topic=1557.0/index.php

Dejavu fonts have permissive license and are available in wide variety: weights, italics, monospace, sans/serif. It has full greek alphabet support as well as math symbols.

SIL has produced not only a good licence, but also fonts: https://software.sil.org/fonts/.

I’ve found out the method for stepping to subsequent characters (advance width property in TTF). And I can extract a “kifont” file (my own format that extracts info from TTF suitable for easy processing, including glyph in SVG format and other character properties and eventually kerning). I can extract with Python 3 and import to Python 2 which was, surprisingly a hurdle.

I’m getting some errors in character stepping related to my processing of the current position, scale, and transformation. It’s a multi-step process to convert from TTF glyph to SVG d-path outline to polygon points to polygons and holes to DRAWSEGMENT polygons with holes.

Along the way, I’ve learned some details about inside/outside definitions in TTF (nonzero) vs SVG (fill mode nonzero or even/odd) and windings (SVG in nonzero fill mode considers the winding of the first polygon to be “inside” with subsequent polygons with opposite winding to be holes).

I’m still seeing issues with hole identification (or maybe the way I’m writing out), and with outline endings possibly related to TTF outline vs SVG path endings—“to Zz or not to Zz, that is the question.”

You might find the font, OCRA could work for you.

It’s one of the original fonts created for Optical Character Recognition and is used by many PCB fab houses. It scales correctly!

Screen Shot 2020-06-10 at 12.41.18 PM

1 Like

A little bit of progress with character spacing and line height. Also implemented affine 2d transformations. Still having trouble with contour ends and identifying polygons and holes As you can see in the screenshot.

Edit; the left is normal KiCAD font/text. On the right is osifont drawn with custom routines in KiCAD polygons.

2 Likes

Getting slowly closer. Here are (hopefully) all the glyphs in Noto-SansRegular. I’m still haven’t addressed identifying the holes, but now the shapes are mostly good. There are some tiny artifacts in curves, invisible when zoomed out, and there are a couple of cases where a polygon artifact I think is causing large blocks across the screen. I think it has to do with rendering the last character on a line, then somehow it thinks there’s a weird piece of polygon way to the left. It’s not on the same glyph (it only happens at the end of a line, sometimes, and seems to be regardless of the actual glyph).

Just wanted to show progress so far. Performance is reasonable, but not stellar. Probably 5 seconds to convert all the NotoSans-Regular glyphs and display them as polygons. Right now, native storage of font data is in SVG format, so I’m converting those SVG paths on the fly to polygons. The preprocessor I wrote converts TTF glyph data into SVG and stores that as font data locally. When this matures and get closer to release, I’ll be distributing the preprocessor as well, so you’ll be able to use any TTF font that you own, and I can point you to some freely available ones (like what has been posted in some of the above comments).

A closer view on some of them. I think the glyphs collecting on top of each other probably don’t have any width information. I’ll have to verify that at some point.

3 Likes

Ok, I think I’ve got the glyph to svg debugged. It had previously generated points outside of what it was supposed to and created little “hairs” on curved areas. I also confirmed my (new) understanding of contours in TTF. Based on that understanding I simplified the glyph to SVG code by not relying on a forward-only processing (iterator). This simplified how to handle the new thing I learned: that the TTF glyph definition is continuous. The first and last point must be joined using the on/off curve rules and create implied control points as necessary. To do this, before processing a specific contour (a TTF glyph is constructed from multiple contours), I look to the start and end points. Depending on their on/off curve status, I copy or create a new point for the beginning or end of the point sequence that matches the on/off curve rules. The result? Smooth contours with no artifacts and simpler code that isolates the begin/end point shenanigans from the bezier and line generation.


This rule is important because I hadn’t recognized that fact in the dozens of websites I referenced: the TTF is continuous from the end of a contour back to the start of the contour specified in the glyf table. It applies to each contour individually, and not all the contours in the glyph.

I think this means I can begin work on fixing the polygon/hole identification mechanism. I’ve been reading a lot on winding rule vs non-zero rule. TTF specifies the non-zero winding rule: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#distinguishing. I know there have been updates to the TTF spec, but I’ll look to follow this rule until I find that it’s been adapted or deprecated. The rule boils down to the same thing for non-self-intersecting contours, but I need to apply it to the multiple contours in one glyph (maybe).

Oh, and I’m still seeing the final glyph on one of the lines generate points way to the left. I’m getting closer to debug that, too.

1 Like

A little more progress. Most of these in NotoSans-Regular look correct. I’ll look more carefully and investigate the few that are not. Also, I’ll work on separating out the accent marks which have zero “advance width” meaning the character position doesn’t advance with those characters.