meerk40t 0.9.3001__py2.py3-none-any.whl → 0.9.7020__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/__init__.py +1 -1
- meerk40t/balormk/balor_params.py +167 -167
- meerk40t/balormk/clone_loader.py +457 -457
- meerk40t/balormk/controller.py +1566 -1512
- meerk40t/balormk/cylindermod.py +64 -0
- meerk40t/balormk/device.py +966 -1959
- meerk40t/balormk/driver.py +778 -591
- meerk40t/balormk/galvo_commands.py +1194 -0
- meerk40t/balormk/gui/balorconfig.py +237 -111
- meerk40t/balormk/gui/balorcontroller.py +191 -184
- meerk40t/balormk/gui/baloroperationproperties.py +116 -115
- meerk40t/balormk/gui/corscene.py +845 -0
- meerk40t/balormk/gui/gui.py +179 -147
- meerk40t/balormk/livelightjob.py +466 -382
- meerk40t/balormk/mock_connection.py +131 -109
- meerk40t/balormk/plugin.py +133 -135
- meerk40t/balormk/usb_connection.py +306 -301
- meerk40t/camera/__init__.py +1 -1
- meerk40t/camera/camera.py +514 -397
- meerk40t/camera/gui/camerapanel.py +1241 -1095
- meerk40t/camera/gui/gui.py +58 -58
- meerk40t/camera/plugin.py +441 -399
- meerk40t/ch341/__init__.py +27 -27
- meerk40t/ch341/ch341device.py +628 -628
- meerk40t/ch341/libusb.py +595 -589
- meerk40t/ch341/mock.py +171 -171
- meerk40t/ch341/windriver.py +157 -157
- meerk40t/constants.py +13 -0
- meerk40t/core/__init__.py +1 -1
- meerk40t/core/bindalias.py +550 -539
- meerk40t/core/core.py +47 -47
- meerk40t/core/cutcode/cubiccut.py +73 -73
- meerk40t/core/cutcode/cutcode.py +315 -312
- meerk40t/core/cutcode/cutgroup.py +141 -137
- meerk40t/core/cutcode/cutobject.py +192 -185
- meerk40t/core/cutcode/dwellcut.py +37 -37
- meerk40t/core/cutcode/gotocut.py +29 -29
- meerk40t/core/cutcode/homecut.py +29 -29
- meerk40t/core/cutcode/inputcut.py +34 -34
- meerk40t/core/cutcode/linecut.py +33 -33
- meerk40t/core/cutcode/outputcut.py +34 -34
- meerk40t/core/cutcode/plotcut.py +335 -335
- meerk40t/core/cutcode/quadcut.py +61 -61
- meerk40t/core/cutcode/rastercut.py +168 -148
- meerk40t/core/cutcode/waitcut.py +34 -34
- meerk40t/core/cutplan.py +1843 -1316
- meerk40t/core/drivers.py +330 -329
- meerk40t/core/elements/align.py +801 -669
- meerk40t/core/elements/branches.py +1858 -1507
- meerk40t/core/elements/clipboard.py +229 -219
- meerk40t/core/elements/element_treeops.py +4595 -2837
- meerk40t/core/elements/element_types.py +125 -105
- meerk40t/core/elements/elements.py +4315 -3617
- meerk40t/core/elements/files.py +117 -64
- meerk40t/core/elements/geometry.py +473 -224
- meerk40t/core/elements/grid.py +467 -316
- meerk40t/core/elements/materials.py +158 -94
- meerk40t/core/elements/notes.py +50 -38
- meerk40t/core/elements/offset_clpr.py +934 -912
- meerk40t/core/elements/offset_mk.py +963 -955
- meerk40t/core/elements/penbox.py +339 -267
- meerk40t/core/elements/placements.py +300 -83
- meerk40t/core/elements/render.py +785 -687
- meerk40t/core/elements/shapes.py +2618 -2092
- meerk40t/core/elements/testcases.py +105 -0
- meerk40t/core/elements/trace.py +651 -563
- meerk40t/core/elements/tree_commands.py +415 -409
- meerk40t/core/elements/undo_redo.py +116 -58
- meerk40t/core/elements/wordlist.py +319 -200
- meerk40t/core/exceptions.py +9 -9
- meerk40t/core/laserjob.py +220 -220
- meerk40t/core/logging.py +63 -63
- meerk40t/core/node/blobnode.py +83 -86
- meerk40t/core/node/bootstrap.py +105 -103
- meerk40t/core/node/branch_elems.py +40 -31
- meerk40t/core/node/branch_ops.py +45 -38
- meerk40t/core/node/branch_regmark.py +48 -41
- meerk40t/core/node/cutnode.py +29 -32
- meerk40t/core/node/effect_hatch.py +375 -257
- meerk40t/core/node/effect_warp.py +398 -0
- meerk40t/core/node/effect_wobble.py +441 -309
- meerk40t/core/node/elem_ellipse.py +404 -309
- meerk40t/core/node/elem_image.py +1082 -801
- meerk40t/core/node/elem_line.py +358 -292
- meerk40t/core/node/elem_path.py +259 -201
- meerk40t/core/node/elem_point.py +129 -102
- meerk40t/core/node/elem_polyline.py +310 -246
- meerk40t/core/node/elem_rect.py +376 -286
- meerk40t/core/node/elem_text.py +445 -418
- meerk40t/core/node/filenode.py +59 -40
- meerk40t/core/node/groupnode.py +138 -74
- meerk40t/core/node/image_processed.py +777 -766
- meerk40t/core/node/image_raster.py +156 -113
- meerk40t/core/node/layernode.py +31 -31
- meerk40t/core/node/mixins.py +135 -107
- meerk40t/core/node/node.py +1427 -1304
- meerk40t/core/node/nutils.py +117 -114
- meerk40t/core/node/op_cut.py +463 -335
- meerk40t/core/node/op_dots.py +296 -251
- meerk40t/core/node/op_engrave.py +414 -311
- meerk40t/core/node/op_image.py +755 -369
- meerk40t/core/node/op_raster.py +787 -522
- meerk40t/core/node/place_current.py +37 -40
- meerk40t/core/node/place_point.py +329 -126
- meerk40t/core/node/refnode.py +58 -47
- meerk40t/core/node/rootnode.py +225 -219
- meerk40t/core/node/util_console.py +48 -48
- meerk40t/core/node/util_goto.py +84 -65
- meerk40t/core/node/util_home.py +61 -61
- meerk40t/core/node/util_input.py +102 -102
- meerk40t/core/node/util_output.py +102 -102
- meerk40t/core/node/util_wait.py +65 -65
- meerk40t/core/parameters.py +709 -707
- meerk40t/core/planner.py +875 -785
- meerk40t/core/plotplanner.py +656 -652
- meerk40t/core/space.py +120 -113
- meerk40t/core/spoolers.py +706 -705
- meerk40t/core/svg_io.py +1836 -1549
- meerk40t/core/treeop.py +534 -445
- meerk40t/core/undos.py +278 -124
- meerk40t/core/units.py +784 -680
- meerk40t/core/view.py +393 -322
- meerk40t/core/webhelp.py +62 -62
- meerk40t/core/wordlist.py +513 -504
- meerk40t/cylinder/cylinder.py +247 -0
- meerk40t/cylinder/gui/cylindersettings.py +41 -0
- meerk40t/cylinder/gui/gui.py +24 -0
- meerk40t/device/__init__.py +1 -1
- meerk40t/device/basedevice.py +322 -123
- meerk40t/device/devicechoices.py +50 -0
- meerk40t/device/dummydevice.py +163 -128
- meerk40t/device/gui/defaultactions.py +618 -602
- meerk40t/device/gui/effectspanel.py +114 -0
- meerk40t/device/gui/formatterpanel.py +253 -290
- meerk40t/device/gui/warningpanel.py +337 -260
- meerk40t/device/mixins.py +13 -13
- meerk40t/dxf/__init__.py +1 -1
- meerk40t/dxf/dxf_io.py +766 -554
- meerk40t/dxf/plugin.py +47 -35
- meerk40t/external_plugins.py +79 -79
- meerk40t/external_plugins_build.py +28 -28
- meerk40t/extra/cag.py +112 -116
- meerk40t/extra/coolant.py +403 -0
- meerk40t/extra/encode_detect.py +204 -0
- meerk40t/extra/ezd.py +1165 -1165
- meerk40t/extra/hershey.py +834 -340
- meerk40t/extra/imageactions.py +322 -316
- meerk40t/extra/inkscape.py +628 -622
- meerk40t/extra/lbrn.py +424 -424
- meerk40t/extra/outerworld.py +283 -0
- meerk40t/extra/param_functions.py +1542 -1556
- meerk40t/extra/potrace.py +257 -253
- meerk40t/extra/serial_exchange.py +118 -0
- meerk40t/extra/updater.py +602 -453
- meerk40t/extra/vectrace.py +147 -146
- meerk40t/extra/winsleep.py +83 -83
- meerk40t/extra/xcs_reader.py +597 -0
- meerk40t/fill/fills.py +781 -335
- meerk40t/fill/patternfill.py +1061 -1061
- meerk40t/fill/patterns.py +614 -567
- meerk40t/grbl/control.py +87 -87
- meerk40t/grbl/controller.py +990 -903
- meerk40t/grbl/device.py +1084 -768
- meerk40t/grbl/driver.py +989 -771
- meerk40t/grbl/emulator.py +532 -497
- meerk40t/grbl/gcodejob.py +783 -767
- meerk40t/grbl/gui/grblconfiguration.py +373 -298
- meerk40t/grbl/gui/grblcontroller.py +485 -271
- meerk40t/grbl/gui/grblhardwareconfig.py +269 -153
- meerk40t/grbl/gui/grbloperationconfig.py +105 -0
- meerk40t/grbl/gui/gui.py +147 -116
- meerk40t/grbl/interpreter.py +44 -44
- meerk40t/grbl/loader.py +22 -22
- meerk40t/grbl/mock_connection.py +56 -56
- meerk40t/grbl/plugin.py +294 -264
- meerk40t/grbl/serial_connection.py +93 -88
- meerk40t/grbl/tcp_connection.py +81 -79
- meerk40t/grbl/ws_connection.py +112 -0
- meerk40t/gui/__init__.py +1 -1
- meerk40t/gui/about.py +2042 -296
- meerk40t/gui/alignment.py +1644 -1608
- meerk40t/gui/autoexec.py +199 -0
- meerk40t/gui/basicops.py +791 -670
- meerk40t/gui/bufferview.py +77 -71
- meerk40t/gui/busy.py +232 -133
- meerk40t/gui/choicepropertypanel.py +1662 -1469
- meerk40t/gui/consolepanel.py +706 -542
- meerk40t/gui/devicepanel.py +687 -581
- meerk40t/gui/dialogoptions.py +110 -107
- meerk40t/gui/executejob.py +316 -306
- meerk40t/gui/fonts.py +90 -90
- meerk40t/gui/functionwrapper.py +252 -0
- meerk40t/gui/gui_mixins.py +729 -0
- meerk40t/gui/guicolors.py +205 -182
- meerk40t/gui/help_assets/help_assets.py +218 -201
- meerk40t/gui/helper.py +154 -0
- meerk40t/gui/hersheymanager.py +1440 -846
- meerk40t/gui/icons.py +3422 -2747
- meerk40t/gui/imagesplitter.py +555 -508
- meerk40t/gui/keymap.py +354 -344
- meerk40t/gui/laserpanel.py +897 -806
- meerk40t/gui/laserrender.py +1470 -1232
- meerk40t/gui/lasertoolpanel.py +805 -793
- meerk40t/gui/magnetoptions.py +436 -0
- meerk40t/gui/materialmanager.py +2944 -0
- meerk40t/gui/materialtest.py +1722 -1694
- meerk40t/gui/mkdebug.py +646 -359
- meerk40t/gui/mwindow.py +163 -140
- meerk40t/gui/navigationpanels.py +2605 -2467
- meerk40t/gui/notes.py +143 -142
- meerk40t/gui/opassignment.py +414 -410
- meerk40t/gui/operation_info.py +310 -299
- meerk40t/gui/plugin.py +500 -328
- meerk40t/gui/position.py +714 -669
- meerk40t/gui/preferences.py +901 -650
- meerk40t/gui/propertypanels/attributes.py +1461 -1131
- meerk40t/gui/propertypanels/blobproperty.py +117 -114
- meerk40t/gui/propertypanels/consoleproperty.py +83 -80
- meerk40t/gui/propertypanels/gotoproperty.py +77 -0
- meerk40t/gui/propertypanels/groupproperties.py +223 -217
- meerk40t/gui/propertypanels/hatchproperty.py +489 -469
- meerk40t/gui/propertypanels/imageproperty.py +2244 -1384
- meerk40t/gui/propertypanels/inputproperty.py +59 -58
- meerk40t/gui/propertypanels/opbranchproperties.py +82 -80
- meerk40t/gui/propertypanels/operationpropertymain.py +1890 -1638
- meerk40t/gui/propertypanels/outputproperty.py +59 -58
- meerk40t/gui/propertypanels/pathproperty.py +389 -380
- meerk40t/gui/propertypanels/placementproperty.py +1214 -383
- meerk40t/gui/propertypanels/pointproperty.py +140 -136
- meerk40t/gui/propertypanels/propertywindow.py +313 -181
- meerk40t/gui/propertypanels/rasterwizardpanels.py +996 -912
- meerk40t/gui/propertypanels/regbranchproperties.py +76 -0
- meerk40t/gui/propertypanels/textproperty.py +770 -755
- meerk40t/gui/propertypanels/waitproperty.py +56 -55
- meerk40t/gui/propertypanels/warpproperty.py +121 -0
- meerk40t/gui/propertypanels/wobbleproperty.py +255 -204
- meerk40t/gui/ribbon.py +2471 -2210
- meerk40t/gui/scene/scene.py +1100 -1051
- meerk40t/gui/scene/sceneconst.py +22 -22
- meerk40t/gui/scene/scenepanel.py +439 -349
- meerk40t/gui/scene/scenespacewidget.py +365 -365
- meerk40t/gui/scene/widget.py +518 -505
- meerk40t/gui/scenewidgets/affinemover.py +215 -215
- meerk40t/gui/scenewidgets/attractionwidget.py +315 -309
- meerk40t/gui/scenewidgets/bedwidget.py +120 -97
- meerk40t/gui/scenewidgets/elementswidget.py +137 -107
- meerk40t/gui/scenewidgets/gridwidget.py +785 -745
- meerk40t/gui/scenewidgets/guidewidget.py +765 -765
- meerk40t/gui/scenewidgets/laserpathwidget.py +66 -66
- meerk40t/gui/scenewidgets/machineoriginwidget.py +86 -86
- meerk40t/gui/scenewidgets/nodeselector.py +28 -28
- meerk40t/gui/scenewidgets/rectselectwidget.py +592 -346
- meerk40t/gui/scenewidgets/relocatewidget.py +33 -33
- meerk40t/gui/scenewidgets/reticlewidget.py +83 -83
- meerk40t/gui/scenewidgets/selectionwidget.py +2958 -2756
- meerk40t/gui/simpleui.py +362 -333
- meerk40t/gui/simulation.py +2451 -2094
- meerk40t/gui/snapoptions.py +208 -203
- meerk40t/gui/spoolerpanel.py +1227 -1180
- meerk40t/gui/statusbarwidgets/defaultoperations.py +480 -353
- meerk40t/gui/statusbarwidgets/infowidget.py +520 -483
- meerk40t/gui/statusbarwidgets/opassignwidget.py +356 -355
- meerk40t/gui/statusbarwidgets/selectionwidget.py +172 -171
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +754 -236
- meerk40t/gui/statusbarwidgets/statusbar.py +272 -260
- meerk40t/gui/statusbarwidgets/statusbarwidget.py +268 -270
- meerk40t/gui/statusbarwidgets/strokewidget.py +267 -251
- meerk40t/gui/themes.py +200 -78
- meerk40t/gui/tips.py +590 -0
- meerk40t/gui/toolwidgets/circlebrush.py +35 -35
- meerk40t/gui/toolwidgets/toolcircle.py +248 -242
- meerk40t/gui/toolwidgets/toolcontainer.py +82 -77
- meerk40t/gui/toolwidgets/tooldraw.py +97 -90
- meerk40t/gui/toolwidgets/toolellipse.py +219 -212
- meerk40t/gui/toolwidgets/toolimagecut.py +25 -132
- meerk40t/gui/toolwidgets/toolline.py +39 -144
- meerk40t/gui/toolwidgets/toollinetext.py +79 -236
- meerk40t/gui/toolwidgets/toollinetext_inline.py +296 -0
- meerk40t/gui/toolwidgets/toolmeasure.py +163 -216
- meerk40t/gui/toolwidgets/toolnodeedit.py +2088 -2074
- meerk40t/gui/toolwidgets/toolnodemove.py +92 -94
- meerk40t/gui/toolwidgets/toolparameter.py +754 -668
- meerk40t/gui/toolwidgets/toolplacement.py +108 -108
- meerk40t/gui/toolwidgets/toolpoint.py +68 -59
- meerk40t/gui/toolwidgets/toolpointlistbuilder.py +294 -0
- meerk40t/gui/toolwidgets/toolpointmove.py +183 -0
- meerk40t/gui/toolwidgets/toolpolygon.py +288 -403
- meerk40t/gui/toolwidgets/toolpolyline.py +38 -196
- meerk40t/gui/toolwidgets/toolrect.py +211 -207
- meerk40t/gui/toolwidgets/toolrelocate.py +72 -72
- meerk40t/gui/toolwidgets/toolribbon.py +598 -113
- meerk40t/gui/toolwidgets/tooltabedit.py +546 -0
- meerk40t/gui/toolwidgets/tooltext.py +98 -89
- meerk40t/gui/toolwidgets/toolvector.py +213 -204
- meerk40t/gui/toolwidgets/toolwidget.py +39 -39
- meerk40t/gui/usbconnect.py +98 -91
- meerk40t/gui/utilitywidgets/buttonwidget.py +18 -18
- meerk40t/gui/utilitywidgets/checkboxwidget.py +90 -90
- meerk40t/gui/utilitywidgets/controlwidget.py +14 -14
- meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +343 -340
- meerk40t/gui/utilitywidgets/debugwidgets.py +148 -0
- meerk40t/gui/utilitywidgets/handlewidget.py +27 -27
- meerk40t/gui/utilitywidgets/harmonograph.py +450 -447
- meerk40t/gui/utilitywidgets/openclosewidget.py +40 -40
- meerk40t/gui/utilitywidgets/rotationwidget.py +54 -54
- meerk40t/gui/utilitywidgets/scalewidget.py +75 -75
- meerk40t/gui/utilitywidgets/seekbarwidget.py +183 -183
- meerk40t/gui/utilitywidgets/togglewidget.py +142 -142
- meerk40t/gui/utilitywidgets/toolbarwidget.py +8 -8
- meerk40t/gui/wordlisteditor.py +985 -931
- meerk40t/gui/wxmeerk40t.py +1447 -1169
- meerk40t/gui/wxmmain.py +5644 -4112
- meerk40t/gui/wxmribbon.py +1591 -1076
- meerk40t/gui/wxmscene.py +1631 -1453
- meerk40t/gui/wxmtree.py +2416 -2089
- meerk40t/gui/wxutils.py +1769 -1099
- meerk40t/gui/zmatrix.py +102 -102
- meerk40t/image/__init__.py +1 -1
- meerk40t/image/dither.py +429 -0
- meerk40t/image/imagetools.py +2793 -2269
- meerk40t/internal_plugins.py +150 -130
- meerk40t/kernel/__init__.py +63 -12
- meerk40t/kernel/channel.py +259 -212
- meerk40t/kernel/context.py +538 -538
- meerk40t/kernel/exceptions.py +41 -41
- meerk40t/kernel/functions.py +463 -414
- meerk40t/kernel/jobs.py +100 -100
- meerk40t/kernel/kernel.py +3828 -3571
- meerk40t/kernel/lifecycles.py +71 -71
- meerk40t/kernel/module.py +49 -49
- meerk40t/kernel/service.py +147 -147
- meerk40t/kernel/settings.py +383 -343
- meerk40t/lihuiyu/controller.py +883 -876
- meerk40t/lihuiyu/device.py +1181 -1069
- meerk40t/lihuiyu/driver.py +1466 -1372
- meerk40t/lihuiyu/gui/gui.py +127 -106
- meerk40t/lihuiyu/gui/lhyaccelgui.py +377 -363
- meerk40t/lihuiyu/gui/lhycontrollergui.py +741 -651
- meerk40t/lihuiyu/gui/lhydrivergui.py +470 -446
- meerk40t/lihuiyu/gui/lhyoperationproperties.py +238 -237
- meerk40t/lihuiyu/gui/tcpcontroller.py +226 -190
- meerk40t/lihuiyu/interpreter.py +53 -53
- meerk40t/lihuiyu/laserspeed.py +450 -450
- meerk40t/lihuiyu/loader.py +90 -90
- meerk40t/lihuiyu/parser.py +404 -404
- meerk40t/lihuiyu/plugin.py +101 -102
- meerk40t/lihuiyu/tcp_connection.py +111 -109
- meerk40t/main.py +231 -165
- meerk40t/moshi/builder.py +788 -781
- meerk40t/moshi/controller.py +505 -499
- meerk40t/moshi/device.py +495 -442
- meerk40t/moshi/driver.py +862 -696
- meerk40t/moshi/gui/gui.py +78 -76
- meerk40t/moshi/gui/moshicontrollergui.py +538 -522
- meerk40t/moshi/gui/moshidrivergui.py +87 -75
- meerk40t/moshi/plugin.py +43 -43
- meerk40t/network/console_server.py +140 -57
- meerk40t/network/kernelserver.py +10 -9
- meerk40t/network/tcp_server.py +142 -140
- meerk40t/network/udp_server.py +103 -77
- meerk40t/network/web_server.py +404 -0
- meerk40t/newly/controller.py +1158 -1144
- meerk40t/newly/device.py +874 -732
- meerk40t/newly/driver.py +540 -412
- meerk40t/newly/gui/gui.py +219 -188
- meerk40t/newly/gui/newlyconfig.py +116 -101
- meerk40t/newly/gui/newlycontroller.py +193 -186
- meerk40t/newly/gui/operationproperties.py +51 -51
- meerk40t/newly/mock_connection.py +82 -82
- meerk40t/newly/newly_params.py +56 -56
- meerk40t/newly/plugin.py +1214 -1246
- meerk40t/newly/usb_connection.py +322 -322
- meerk40t/rotary/gui/gui.py +52 -46
- meerk40t/rotary/gui/rotarysettings.py +240 -232
- meerk40t/rotary/rotary.py +202 -98
- meerk40t/ruida/control.py +291 -91
- meerk40t/ruida/controller.py +138 -1088
- meerk40t/ruida/device.py +676 -231
- meerk40t/ruida/driver.py +534 -472
- meerk40t/ruida/emulator.py +1494 -1491
- meerk40t/ruida/exceptions.py +4 -4
- meerk40t/ruida/gui/gui.py +71 -76
- meerk40t/ruida/gui/ruidaconfig.py +239 -72
- meerk40t/ruida/gui/ruidacontroller.py +187 -184
- meerk40t/ruida/gui/ruidaoperationproperties.py +48 -47
- meerk40t/ruida/loader.py +54 -52
- meerk40t/ruida/mock_connection.py +57 -109
- meerk40t/ruida/plugin.py +124 -87
- meerk40t/ruida/rdjob.py +2084 -945
- meerk40t/ruida/serial_connection.py +116 -0
- meerk40t/ruida/tcp_connection.py +146 -0
- meerk40t/ruida/udp_connection.py +73 -0
- meerk40t/svgelements.py +9671 -9669
- meerk40t/tools/driver_to_path.py +584 -579
- meerk40t/tools/geomstr.py +5583 -4680
- meerk40t/tools/jhfparser.py +357 -292
- meerk40t/tools/kerftest.py +904 -890
- meerk40t/tools/livinghinges.py +1168 -1033
- meerk40t/tools/pathtools.py +987 -949
- meerk40t/tools/pmatrix.py +234 -0
- meerk40t/tools/pointfinder.py +942 -942
- meerk40t/tools/polybool.py +941 -940
- meerk40t/tools/rasterplotter.py +1660 -547
- meerk40t/tools/shxparser.py +1047 -901
- meerk40t/tools/ttfparser.py +726 -446
- meerk40t/tools/zinglplotter.py +595 -593
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/LICENSE +21 -21
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/METADATA +150 -139
- meerk40t-0.9.7020.dist-info/RECORD +446 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/WHEEL +1 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/top_level.txt +0 -1
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/zip-safe +1 -1
- meerk40t/balormk/elementlightjob.py +0 -159
- meerk40t-0.9.3001.dist-info/RECORD +0 -437
- test/bootstrap.py +0 -63
- test/test_cli.py +0 -12
- test/test_core_cutcode.py +0 -418
- test/test_core_elements.py +0 -144
- test/test_core_plotplanner.py +0 -397
- test/test_core_viewports.py +0 -312
- test/test_drivers_grbl.py +0 -108
- test/test_drivers_lihuiyu.py +0 -443
- test/test_drivers_newly.py +0 -113
- test/test_element_degenerate_points.py +0 -43
- test/test_elements_classify.py +0 -97
- test/test_elements_penbox.py +0 -22
- test/test_file_svg.py +0 -176
- test/test_fill.py +0 -155
- test/test_geomstr.py +0 -1523
- test/test_geomstr_nodes.py +0 -18
- test/test_imagetools_actualize.py +0 -306
- test/test_imagetools_wizard.py +0 -258
- test/test_kernel.py +0 -200
- test/test_laser_speeds.py +0 -3303
- test/test_length.py +0 -57
- test/test_lifecycle.py +0 -66
- test/test_operations.py +0 -251
- test/test_operations_hatch.py +0 -57
- test/test_ruida.py +0 -19
- test/test_spooler.py +0 -22
- test/test_tools_rasterplotter.py +0 -29
- test/test_wobble.py +0 -133
- test/test_zingl.py +0 -124
- {test → meerk40t/cylinder}/__init__.py +0 -0
- /meerk40t/{core/element_commands.py → cylinder/gui/__init__.py} +0 -0
- {meerk40t-0.9.3001.dist-info → meerk40t-0.9.7020.dist-info}/entry_points.txt +0 -0
meerk40t/extra/hershey.py
CHANGED
@@ -1,340 +1,834 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
from
|
5
|
-
from
|
6
|
-
|
7
|
-
from meerk40t.
|
8
|
-
from meerk40t.
|
9
|
-
from meerk40t.
|
10
|
-
from meerk40t.tools.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
self.
|
22
|
-
|
23
|
-
|
24
|
-
self.
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
#
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
1
|
+
import os
|
2
|
+
import platform
|
3
|
+
from functools import lru_cache
|
4
|
+
from glob import glob
|
5
|
+
from os.path import basename, exists, join, realpath, splitext
|
6
|
+
|
7
|
+
from meerk40t.core.node.elem_path import PathNode
|
8
|
+
from meerk40t.core.node.node import Fillrule, Linejoin
|
9
|
+
from meerk40t.core.units import UNITS_PER_INCH, Length
|
10
|
+
from meerk40t.tools.geomstr import BeamTable, Geomstr
|
11
|
+
from meerk40t.tools.jhfparser import JhfFont
|
12
|
+
from meerk40t.tools.shxparser import ShxFont, ShxFontParseError
|
13
|
+
from meerk40t.tools.ttfparser import TrueTypeFont, TTFParsingError
|
14
|
+
|
15
|
+
# import numpy as np
|
16
|
+
|
17
|
+
|
18
|
+
class FontPath:
|
19
|
+
def __init__(self, weld):
|
20
|
+
self.total_list = list()
|
21
|
+
self.total_geometry = Geomstr()
|
22
|
+
self.geom = Geomstr()
|
23
|
+
self._index = 0
|
24
|
+
self.start = None
|
25
|
+
self.weld = weld
|
26
|
+
|
27
|
+
def character_end(self):
|
28
|
+
if self.weld:
|
29
|
+
self.geom.as_interpolated_points()
|
30
|
+
c = Geomstr()
|
31
|
+
for sp in self.geom.as_subpaths():
|
32
|
+
for segs in sp.as_interpolated_segments(interpolate=10):
|
33
|
+
c.polyline(segs)
|
34
|
+
c.end()
|
35
|
+
c.flag_settings(flag=self._index)
|
36
|
+
self._index += 1
|
37
|
+
self.total_geometry.append(c)
|
38
|
+
else:
|
39
|
+
self.total_geometry.append(self.geom)
|
40
|
+
self.geom.clear()
|
41
|
+
|
42
|
+
@property
|
43
|
+
def geometry(self):
|
44
|
+
if not self.weld:
|
45
|
+
return self.total_geometry
|
46
|
+
bt = BeamTable(self.total_geometry.simplify())
|
47
|
+
union = bt.union(*list(range(self._index)))
|
48
|
+
# union.remove_0_length()
|
49
|
+
union.greedy_distance()
|
50
|
+
return union.simplify()
|
51
|
+
|
52
|
+
def new_path(self):
|
53
|
+
self.geom.end()
|
54
|
+
|
55
|
+
def move(self, x, y):
|
56
|
+
# self.geom.move((x, -y))
|
57
|
+
if self.start is not None:
|
58
|
+
self.geom.end()
|
59
|
+
self.start = x - 1j * y
|
60
|
+
|
61
|
+
def line(self, x0, y0, x1, y1):
|
62
|
+
# self.path.line((x1, -y1))
|
63
|
+
end = x1 - 1j * y1
|
64
|
+
self.geom.line(self.start, end)
|
65
|
+
self.start = end
|
66
|
+
|
67
|
+
def quad(self, x0, y0, x1, y1, x2, y2):
|
68
|
+
# self.path.quad((x1, -y1), (x2, -y2))
|
69
|
+
control = x1 - 1j * y1
|
70
|
+
end = x2 - 1j * y2
|
71
|
+
self.geom.quad(self.start, control, end)
|
72
|
+
self.start = end
|
73
|
+
|
74
|
+
def cubic(self, x0, y0, x1, y1, x2, y2, x3, y3):
|
75
|
+
# self.path.cubic((x1, -y1), (x2, -y2), (x3, -y3))
|
76
|
+
control0 = x1 - 1j * y1
|
77
|
+
control1 = x2 - 1j * y2
|
78
|
+
end = x3 - 1j * y3
|
79
|
+
self.geom.cubic(self.start, control0, control1, end)
|
80
|
+
self.start = end
|
81
|
+
|
82
|
+
def close(self):
|
83
|
+
self.geom.close()
|
84
|
+
|
85
|
+
def arc(self, x0, y0, cx, cy, x1, y1):
|
86
|
+
# arc = Arc(start=(x0, -y0), control=(cx, -cy), end=(x1, -y1))
|
87
|
+
# self.path += arc
|
88
|
+
control = cx - 1j * cy
|
89
|
+
end = x1 - 1j * y1
|
90
|
+
self.geom.arc(self.start, control, end)
|
91
|
+
self.start = end
|
92
|
+
|
93
|
+
|
94
|
+
class Meerk40tFonts:
|
95
|
+
"""
|
96
|
+
Main Interface to access vector fonts.
|
97
|
+
Supported formats:
|
98
|
+
ttf - TrueType fonts
|
99
|
+
shx - AutoCad fonts
|
100
|
+
jhf - Hershey fonts
|
101
|
+
"""
|
102
|
+
|
103
|
+
def __init__(self, context, **kwds):
|
104
|
+
self.context = context
|
105
|
+
self._available_fonts = None
|
106
|
+
|
107
|
+
@property
|
108
|
+
def fonts_registered(self):
|
109
|
+
fonts = {
|
110
|
+
"shx": ("Autocad", ShxFont, None),
|
111
|
+
"jhf": ("Hershey", JhfFont, None),
|
112
|
+
"ttf": ("TrueType", TrueTypeFont, TrueTypeFont.query_name),
|
113
|
+
}
|
114
|
+
return fonts
|
115
|
+
|
116
|
+
@property
|
117
|
+
def font_directory(self):
|
118
|
+
safe_dir = self.context.kernel.os_information["WORKDIR"]
|
119
|
+
self.context.setting(str, "font_directory", safe_dir)
|
120
|
+
fontdir = self.context.font_directory
|
121
|
+
if not exists(fontdir):
|
122
|
+
# Fallback, something strange happened...
|
123
|
+
fontdir = safe_dir
|
124
|
+
self.context.font_directory = fontdir
|
125
|
+
return fontdir
|
126
|
+
|
127
|
+
@font_directory.setter
|
128
|
+
def font_directory(self, value):
|
129
|
+
if not exists(value):
|
130
|
+
# We cant allow a non-valid directory
|
131
|
+
value = self.context.kernel.os_information["WORKDIR"]
|
132
|
+
self.context.setting(str, "font_directory", value)
|
133
|
+
self.context.font_directory = value
|
134
|
+
self._available_fonts = None
|
135
|
+
|
136
|
+
@property
|
137
|
+
def cache_file(self):
|
138
|
+
return join(self.font_directory, "fonts.cache")
|
139
|
+
|
140
|
+
def reset_cache(self):
|
141
|
+
fn = self.cache_file
|
142
|
+
try:
|
143
|
+
os.remove(fn)
|
144
|
+
except (OSError, FileNotFoundError, PermissionError):
|
145
|
+
pass
|
146
|
+
self._available_fonts = None
|
147
|
+
p = self.available_fonts()
|
148
|
+
|
149
|
+
def have_hershey_fonts(self):
|
150
|
+
p = self.available_fonts()
|
151
|
+
return len(p) > 0
|
152
|
+
|
153
|
+
@lru_cache(maxsize=512)
|
154
|
+
def get_font_information(self, full_file_name):
|
155
|
+
filename, file_extension = splitext(full_file_name)
|
156
|
+
if len(file_extension) == 0:
|
157
|
+
return None
|
158
|
+
# Remove dot...
|
159
|
+
file_extension = file_extension[1:].lower()
|
160
|
+
try:
|
161
|
+
item = self.fonts_registered[file_extension]
|
162
|
+
except KeyError:
|
163
|
+
return None
|
164
|
+
if item[2]:
|
165
|
+
return item[2](full_file_name)
|
166
|
+
return None
|
167
|
+
|
168
|
+
def _get_full_info(self, short):
|
169
|
+
s_lower = short.lower()
|
170
|
+
p = self.available_fonts()
|
171
|
+
for info in p:
|
172
|
+
# We don't care about capitalisation
|
173
|
+
f_lower = info[0].lower()
|
174
|
+
if f_lower.endswith(s_lower):
|
175
|
+
return info
|
176
|
+
return None
|
177
|
+
|
178
|
+
def is_system_font(self, short):
|
179
|
+
info = self._get_full_info(short)
|
180
|
+
if info:
|
181
|
+
return info[4]
|
182
|
+
return True
|
183
|
+
|
184
|
+
def face_to_full_name(self, short):
|
185
|
+
s_lower = short.lower()
|
186
|
+
p = self.available_fonts()
|
187
|
+
options = ("regular", "bold", "italic")
|
188
|
+
candidates = []
|
189
|
+
for info in p:
|
190
|
+
# We don't care about capitalisation
|
191
|
+
f_lower = info[1].lower()
|
192
|
+
# print (f"Comparing {s_lower} to {f_lower} ({info[1]}, {info[2]}, {info[3]})")
|
193
|
+
if f_lower == s_lower:
|
194
|
+
return info[0]
|
195
|
+
for idx, opt in enumerate(options):
|
196
|
+
if f"{s_lower} {opt}" == f_lower:
|
197
|
+
# print (f"Appending {idx} {f_lower}")
|
198
|
+
candidates.append((idx, info[0]))
|
199
|
+
if len(candidates):
|
200
|
+
candidates.sort(key=lambda e: e[0])
|
201
|
+
return candidates[0][1]
|
202
|
+
return None
|
203
|
+
|
204
|
+
def full_name(self, short):
|
205
|
+
info = self._get_full_info(short)
|
206
|
+
if info:
|
207
|
+
return info[0]
|
208
|
+
return None
|
209
|
+
|
210
|
+
def short_name(self, fullname):
|
211
|
+
return basename(fullname)
|
212
|
+
|
213
|
+
@lru_cache(maxsize=128)
|
214
|
+
def cached_fontclass(self, fontname):
|
215
|
+
registered_fonts = self.fonts_registered
|
216
|
+
if not exists(fontname):
|
217
|
+
return
|
218
|
+
try:
|
219
|
+
filename, file_extension = splitext(fontname)
|
220
|
+
if len(file_extension) > 0:
|
221
|
+
# Remove dot...
|
222
|
+
file_extension = file_extension[1:].lower()
|
223
|
+
item = registered_fonts[file_extension]
|
224
|
+
fontclass = item[1]
|
225
|
+
except (KeyError, IndexError):
|
226
|
+
# channel(_("Unknown fonttype {ext}").format(ext=file_extension))
|
227
|
+
# print ("unknown fonttype, exit")
|
228
|
+
return
|
229
|
+
# print("Nearly there, all fonts checked...")
|
230
|
+
try:
|
231
|
+
cfont = fontclass(fontname)
|
232
|
+
except (TTFParsingError, ShxFontParseError):
|
233
|
+
return None
|
234
|
+
return cfont
|
235
|
+
|
236
|
+
def validate_node(self, node):
|
237
|
+
# After a svg load the attributes are still a string...
|
238
|
+
if not hasattr(node, "mkfontsize"):
|
239
|
+
return
|
240
|
+
if isinstance(node.mkfontsize, str):
|
241
|
+
try:
|
242
|
+
value = float(node.mkfontsize)
|
243
|
+
except ValueError:
|
244
|
+
value = Length("20px")
|
245
|
+
node.mkfontsize = value
|
246
|
+
# if not hasattr(node, "mkcoordx"):
|
247
|
+
# node.mkcoordx = 0
|
248
|
+
# if not hasattr(node, "mkcoordy"):
|
249
|
+
# node.mkcoordy = 0
|
250
|
+
# if isinstance(node.mkcoordx, str):
|
251
|
+
# try:
|
252
|
+
# value = float(node.mkcoordx)
|
253
|
+
# except ValueError:
|
254
|
+
# value = 0
|
255
|
+
# node.mkcoordx = value
|
256
|
+
# if isinstance(node.mkcoordy, str):
|
257
|
+
# try:
|
258
|
+
# value = float(node.mkcoordy)
|
259
|
+
# except ValueError:
|
260
|
+
# value = 0
|
261
|
+
# node.mkcoordy = value
|
262
|
+
|
263
|
+
def update(self, context=None, node=None):
|
264
|
+
# We need to check for the validity ourselves...
|
265
|
+
if (
|
266
|
+
hasattr(node, "mktext")
|
267
|
+
and hasattr(node, "mkfont")
|
268
|
+
and hasattr(node, "mkfontsize")
|
269
|
+
):
|
270
|
+
self.update_linetext(node, node.mktext)
|
271
|
+
|
272
|
+
def update_linetext(self, node, newtext):
|
273
|
+
# print ("Update Linetext")
|
274
|
+
if node is None:
|
275
|
+
# print ("node is none, exit")
|
276
|
+
return
|
277
|
+
if not hasattr(node, "mkfont"):
|
278
|
+
# print ("no font attr, exit")
|
279
|
+
return
|
280
|
+
if not hasattr(node, "mkfontsize"):
|
281
|
+
# print ("no fontsize attr, exit")
|
282
|
+
return
|
283
|
+
h_spacing = None
|
284
|
+
if hasattr(node, "mkfontspacing"):
|
285
|
+
try:
|
286
|
+
h_spacing = float(node.mkfontspacing)
|
287
|
+
except AttributeError:
|
288
|
+
pass
|
289
|
+
if h_spacing is None:
|
290
|
+
h_spacing = 1
|
291
|
+
v_spacing = None
|
292
|
+
if hasattr(node, "mklinegap"):
|
293
|
+
try:
|
294
|
+
v_spacing = float(node.mklinegap)
|
295
|
+
except AttributeError:
|
296
|
+
pass
|
297
|
+
if v_spacing is None:
|
298
|
+
v_spacing = 1.1
|
299
|
+
|
300
|
+
align = None
|
301
|
+
if hasattr(node, "mkalign"):
|
302
|
+
align = node.mkalign
|
303
|
+
if align is None or align not in ("start", "middle", "end"):
|
304
|
+
align = "start"
|
305
|
+
|
306
|
+
weld = None
|
307
|
+
if hasattr(node, "mkfontweld"):
|
308
|
+
try:
|
309
|
+
weld = bool(node.mkfontweld)
|
310
|
+
except ValueError:
|
311
|
+
pass
|
312
|
+
if weld is None:
|
313
|
+
weld = False
|
314
|
+
# from time import perf_counter
|
315
|
+
# _t0 = perf_counter()
|
316
|
+
# oldtext = getattr(node, "_translated_text", "")
|
317
|
+
fontname = node.mkfont
|
318
|
+
fontsize = node.mkfontsize
|
319
|
+
# old_color = node.stroke
|
320
|
+
# old_strokewidth = node.stroke_width
|
321
|
+
# old_strokescaled = node._stroke_scaled
|
322
|
+
fullfont = self.full_name(fontname)
|
323
|
+
if fullfont is None:
|
324
|
+
# This font does not exist in our environment
|
325
|
+
return
|
326
|
+
# Make sure any paths are removed
|
327
|
+
fontname = self.short_name(fontname)
|
328
|
+
|
329
|
+
cfont = self.cached_fontclass(fullfont)
|
330
|
+
if cfont is None:
|
331
|
+
# This font does not exist in our environment
|
332
|
+
return
|
333
|
+
|
334
|
+
# _t1 = perf_counter()
|
335
|
+
|
336
|
+
path = FontPath(weld)
|
337
|
+
# print (f"Path={path}, text={remainder}, font-size={font_size}")
|
338
|
+
horizontal = True
|
339
|
+
mytext = self.context.elements.wordlist_translate(newtext)
|
340
|
+
cfont.render(
|
341
|
+
path, mytext, horizontal, float(fontsize), h_spacing, v_spacing, align
|
342
|
+
)
|
343
|
+
if hasattr(cfont, "line_information"):
|
344
|
+
# Store the relative start / end positions of the text lines
|
345
|
+
# for any interested party...
|
346
|
+
node._line_information = cfont.line_information()
|
347
|
+
# _t2 = perf_counter()
|
348
|
+
olda = node.matrix.a
|
349
|
+
oldb = node.matrix.b
|
350
|
+
oldc = node.matrix.c
|
351
|
+
oldd = node.matrix.d
|
352
|
+
olde = node.matrix.e
|
353
|
+
oldf = node.matrix.f
|
354
|
+
node.geometry = path.geometry
|
355
|
+
node.matrix.a = olda
|
356
|
+
node.matrix.b = oldb
|
357
|
+
node.matrix.c = oldc
|
358
|
+
node.matrix.d = oldd
|
359
|
+
node.matrix.e = olde
|
360
|
+
node.matrix.f = oldf
|
361
|
+
# print (f"x={node.mkcoordx}, y={node.mkcoordy}")
|
362
|
+
# node.path.transform = Matrix.translate(node.mkcoordx, node.mkcoordy)
|
363
|
+
# print (f"Updated: from {oldtext} -> {mytext}")
|
364
|
+
node.mktext = newtext
|
365
|
+
node.mkfont = fontname
|
366
|
+
node._translated_text = mytext
|
367
|
+
# _t3 = perf_counter()
|
368
|
+
tlines = mytext.split("\n")
|
369
|
+
tlabel = f"Text: {tlines[0]}"
|
370
|
+
if node.label is None or node.label.startswith("Text: "):
|
371
|
+
node.label = tlabel
|
372
|
+
|
373
|
+
node.altered()
|
374
|
+
# _t4 = perf_counter()
|
375
|
+
# print (f"Readfont: {_t1 -_t0:.2f}s, render: {_t2 -_t1:.2f}s, path: {_t3 -_t2:.2f}s, alter: {_t4 -_t3:.2f}s, total={_t4 -_t0:.2f}s")
|
376
|
+
|
377
|
+
def _validate_font(self, font):
|
378
|
+
"""
|
379
|
+
Check if the given font value is valid.
|
380
|
+
|
381
|
+
@param font:
|
382
|
+
@return:
|
383
|
+
"""
|
384
|
+
if not font:
|
385
|
+
return False
|
386
|
+
# Let's check whether it's valid...
|
387
|
+
font_path = self.full_name(font)
|
388
|
+
if not font_path:
|
389
|
+
# Font isn't found as processed.
|
390
|
+
return False
|
391
|
+
try:
|
392
|
+
self.cached_fontclass(font_path)
|
393
|
+
except TTFParsingError:
|
394
|
+
# Font could not parse.
|
395
|
+
return False
|
396
|
+
return True
|
397
|
+
|
398
|
+
def _try_candidates(self):
|
399
|
+
# No preferred font set, let's try a couple of candidates...
|
400
|
+
candidates = (
|
401
|
+
"arial.ttf",
|
402
|
+
"opensans_regular.ttf",
|
403
|
+
"timesr.jhf",
|
404
|
+
"romant.shx",
|
405
|
+
"rowmans.jhf",
|
406
|
+
"FUTURA.SHX",
|
407
|
+
)
|
408
|
+
for fname in candidates:
|
409
|
+
if self._validate_font(fname):
|
410
|
+
return self.full_name(fname)
|
411
|
+
return None
|
412
|
+
|
413
|
+
def _try_available(self):
|
414
|
+
if not self.available_fonts():
|
415
|
+
return None
|
416
|
+
for i, font in enumerate(self._available_fonts):
|
417
|
+
candidate = font[0]
|
418
|
+
if self._validate_font(candidate):
|
419
|
+
return candidate
|
420
|
+
return None
|
421
|
+
|
422
|
+
def retrieve_font(self, font):
|
423
|
+
if not self._validate_font(font) and font is not None:
|
424
|
+
# Is the given font valid?
|
425
|
+
# It could still translate to a valid name
|
426
|
+
font_path = self.face_to_full_name(font)
|
427
|
+
if font_path:
|
428
|
+
font = self.short_name(font_path)
|
429
|
+
return font, font_path
|
430
|
+
font = None
|
431
|
+
if not font:
|
432
|
+
# No valid font, try last font.
|
433
|
+
font = self.context.last_font
|
434
|
+
if not self._validate_font(font):
|
435
|
+
font = None
|
436
|
+
if not font:
|
437
|
+
# Still not valid? Try preselected candidates.
|
438
|
+
font = self._try_candidates()
|
439
|
+
if not font:
|
440
|
+
# You know, I take anything at this point...
|
441
|
+
font = self._try_available()
|
442
|
+
|
443
|
+
if not font:
|
444
|
+
# No font could be located.
|
445
|
+
return None, None
|
446
|
+
|
447
|
+
# We have our valid font.
|
448
|
+
font_path = self.full_name(font)
|
449
|
+
font = self.short_name(font)
|
450
|
+
return font, font_path
|
451
|
+
|
452
|
+
def create_linetext_node(
|
453
|
+
self, x, y, text, font=None, font_size=None, font_spacing=1.0, align="start",
|
454
|
+
):
|
455
|
+
if font_size is None:
|
456
|
+
font_size = Length("20px")
|
457
|
+
if font_spacing is None:
|
458
|
+
font_spacing = 1
|
459
|
+
self.context.setting(str, "last_font", "")
|
460
|
+
font, font_path = self.retrieve_font(font)
|
461
|
+
|
462
|
+
if not font:
|
463
|
+
# No font could be located.
|
464
|
+
return None
|
465
|
+
|
466
|
+
# We tried everything if there is a font, set it to last_font.
|
467
|
+
self.context.last_font = font
|
468
|
+
|
469
|
+
# We have our valid font.
|
470
|
+
|
471
|
+
horizontal = True
|
472
|
+
cfont = self.cached_fontclass(font_path)
|
473
|
+
weld = False
|
474
|
+
|
475
|
+
# Render the font.
|
476
|
+
try:
|
477
|
+
path = FontPath(weld)
|
478
|
+
# print (f"Path={path}, text={remainder}, font-size={font_size}")
|
479
|
+
mytext = self.context.elements.wordlist_translate(text)
|
480
|
+
cfont.render(path, mytext, horizontal=horizontal, font_size=float(font_size), h_spacing=font_spacing, align=align)
|
481
|
+
except (AttributeError, ShxFontParseError):
|
482
|
+
# Could not parse path.
|
483
|
+
pass
|
484
|
+
|
485
|
+
tlines = mytext.split("\n")
|
486
|
+
tlabel = "Text: {mktext}"
|
487
|
+
# Create the node.
|
488
|
+
path_node = PathNode(
|
489
|
+
geometry=path.geometry,
|
490
|
+
stroke=self.context.elements.default_stroke,
|
491
|
+
stroke_width=self.context.elements.default_strokewidth,
|
492
|
+
fill=self.context.elements.default_fill,
|
493
|
+
fillrule=Fillrule.FILLRULE_NONZERO,
|
494
|
+
linejoin=Linejoin.JOIN_BEVEL,
|
495
|
+
label=tlabel,
|
496
|
+
)
|
497
|
+
path_node.matrix.post_translate(x, y)
|
498
|
+
path_node.mkfont = font
|
499
|
+
path_node.mkfontsize = float(font_size)
|
500
|
+
path_node.mkfontspacing = float(font_spacing)
|
501
|
+
path_node.mkfontweld = weld
|
502
|
+
path_node.mkalign = align
|
503
|
+
path_node.mklinegap = 1.1
|
504
|
+
path_node.mktext = text
|
505
|
+
path_node._translated_text = mytext
|
506
|
+
path_node.mkcoordx = x
|
507
|
+
path_node.mkcoordy = y
|
508
|
+
if hasattr(cfont, "line_information"):
|
509
|
+
# Store the relative start / end positions of the text lines
|
510
|
+
# for any interested party...
|
511
|
+
path_node._line_information = cfont.line_information()
|
512
|
+
|
513
|
+
return path_node
|
514
|
+
|
515
|
+
def preview_file(self, fontfile):
|
516
|
+
def create_preview_image(fontfile, bmpfile, bitmap_format):
|
517
|
+
from math import isinf
|
518
|
+
|
519
|
+
simplefont = basename(fontfile)
|
520
|
+
pattern = "The quick brown fox..."
|
521
|
+
try:
|
522
|
+
node = self.create_linetext_node(
|
523
|
+
0, 0, pattern, font=simplefont, font_size=Length("12pt")
|
524
|
+
)
|
525
|
+
except Exception:
|
526
|
+
# We may encounter an IndexError, a ValueError or an error thrown by struct
|
527
|
+
# The latter cannot be named? So a global except...
|
528
|
+
# print (f"Node creation failed: {e}")
|
529
|
+
return False
|
530
|
+
if node is None:
|
531
|
+
return False
|
532
|
+
if node.bounds is None:
|
533
|
+
return False
|
534
|
+
make_raster = self.context.elements.lookup("render-op/make_raster")
|
535
|
+
if make_raster is None:
|
536
|
+
return False
|
537
|
+
xmin, ymin, xmax, ymax = node.bounds
|
538
|
+
if isinf(xmin):
|
539
|
+
# No bounds for selected elements
|
540
|
+
return False
|
541
|
+
width = xmax - xmin
|
542
|
+
height = ymax - ymin
|
543
|
+
dpi = 150
|
544
|
+
dots_per_units = dpi / UNITS_PER_INCH
|
545
|
+
new_width = width * dots_per_units
|
546
|
+
new_height = height * dots_per_units
|
547
|
+
new_height = max(new_height, 1)
|
548
|
+
new_width = max(new_width, 1)
|
549
|
+
try:
|
550
|
+
bitmap = make_raster(
|
551
|
+
[node],
|
552
|
+
bounds=node.bounds,
|
553
|
+
width=new_width,
|
554
|
+
height=new_height,
|
555
|
+
bitmap=True,
|
556
|
+
)
|
557
|
+
except Exception:
|
558
|
+
# print (f"Raster failed: {e}")
|
559
|
+
# Invalid path or whatever...
|
560
|
+
return False
|
561
|
+
try:
|
562
|
+
bitmap.SaveFile(bmpfile, bitmap_format)
|
563
|
+
except (OSError, RuntimeError, PermissionError, FileNotFoundError):
|
564
|
+
# print (f"Save failed: {e}")
|
565
|
+
return False
|
566
|
+
return True
|
567
|
+
|
568
|
+
bitmap = None
|
569
|
+
try:
|
570
|
+
import wx
|
571
|
+
except ImportError:
|
572
|
+
return None
|
573
|
+
base, ext = splitext(basename(fontfile))
|
574
|
+
bmpfile = join(self.font_directory, base + ".png")
|
575
|
+
if not exists(bmpfile):
|
576
|
+
__ = create_preview_image(fontfile, bmpfile, wx.BITMAP_TYPE_PNG)
|
577
|
+
if exists(bmpfile):
|
578
|
+
bitmap = wx.Bitmap()
|
579
|
+
bitmap.LoadFile(bmpfile, wx.BITMAP_TYPE_PNG)
|
580
|
+
return bitmap
|
581
|
+
|
582
|
+
def available_fonts(self):
|
583
|
+
if self._available_fonts is not None:
|
584
|
+
return self._available_fonts
|
585
|
+
|
586
|
+
# Return a tuple of two values
|
587
|
+
# from time import perf_counter
|
588
|
+
|
589
|
+
_ = self.context.kernel.translation
|
590
|
+
# t0 = perf_counter()
|
591
|
+
self._available_fonts = []
|
592
|
+
|
593
|
+
cache = self.cache_file
|
594
|
+
if exists(cache):
|
595
|
+
try:
|
596
|
+
with open(cache, "r", encoding="utf-8") as f:
|
597
|
+
while True:
|
598
|
+
line = f.readline()
|
599
|
+
if not line:
|
600
|
+
break
|
601
|
+
line = line.strip()
|
602
|
+
parts = line.split("|")
|
603
|
+
if len(parts) > 4:
|
604
|
+
flag = False
|
605
|
+
if parts[4].lower in ("true", "1"):
|
606
|
+
flag = True
|
607
|
+
self._available_fonts.append(
|
608
|
+
(
|
609
|
+
parts[0],
|
610
|
+
parts[1],
|
611
|
+
parts[2],
|
612
|
+
parts[3],
|
613
|
+
flag,
|
614
|
+
)
|
615
|
+
)
|
616
|
+
except (OSError, FileNotFoundError, PermissionError):
|
617
|
+
self._available_fonts = []
|
618
|
+
if len(self._available_fonts):
|
619
|
+
# t1 = perf_counter()
|
620
|
+
# print (f"Cached, took {t1 - t0:.2f}sec")
|
621
|
+
return self._available_fonts
|
622
|
+
|
623
|
+
busy = self.context.kernel.busyinfo
|
624
|
+
busy.start(msg=_("Reading system fonts..."))
|
625
|
+
directories = []
|
626
|
+
directories.append(self.font_directory)
|
627
|
+
for d in self.context.system_font_directories:
|
628
|
+
directories.append(d)
|
629
|
+
# Walk through all folders recursively
|
630
|
+
found = dict()
|
631
|
+
font_types = self.fonts_registered
|
632
|
+
filelist = []
|
633
|
+
for idx, fontpath in enumerate(directories):
|
634
|
+
busy.change(msg=fontpath, keep=1)
|
635
|
+
busy.show()
|
636
|
+
|
637
|
+
systemfont = idx != 0
|
638
|
+
for p in font_types:
|
639
|
+
found[p] = 0
|
640
|
+
try:
|
641
|
+
for root, dirs, files in os.walk(fontpath):
|
642
|
+
for filename in files:
|
643
|
+
if not filename:
|
644
|
+
continue
|
645
|
+
short = basename(filename)
|
646
|
+
if not short:
|
647
|
+
continue
|
648
|
+
full_name = join(root, filename)
|
649
|
+
test = filename.lower()
|
650
|
+
for p in font_types:
|
651
|
+
if test.endswith(p):
|
652
|
+
if filename not in filelist:
|
653
|
+
font_family = ""
|
654
|
+
font_subfamily = ""
|
655
|
+
face_name = short
|
656
|
+
info = self.get_font_information(full_name)
|
657
|
+
if info:
|
658
|
+
# Tuple with font_family, font_subfamily, face_name
|
659
|
+
font_family, font_subfamily, face_name = info
|
660
|
+
else:
|
661
|
+
entry = font_types[p]
|
662
|
+
font_family = entry[0]
|
663
|
+
if face_name is None:
|
664
|
+
face_name = short
|
665
|
+
self._available_fonts.append(
|
666
|
+
(
|
667
|
+
str(full_name),
|
668
|
+
str(face_name),
|
669
|
+
font_family,
|
670
|
+
font_subfamily,
|
671
|
+
systemfont,
|
672
|
+
)
|
673
|
+
)
|
674
|
+
# print (face_name, font_family, font_subfamily, full_name)
|
675
|
+
filelist.append(filename)
|
676
|
+
found[p] += 1
|
677
|
+
break
|
678
|
+
except (OSError, FileNotFoundError, PermissionError):
|
679
|
+
continue
|
680
|
+
# for key, value in found.items():
|
681
|
+
# print(f"{key}: {value} - {fontpath}")
|
682
|
+
self._available_fonts.sort(key=lambda e: e[1])
|
683
|
+
try:
|
684
|
+
with open(cache, "w", encoding="utf-8") as f:
|
685
|
+
for p in self._available_fonts:
|
686
|
+
f.write(f"{p[0]}|{p[1]}|{p[2]}|{p[3]}|{p[4]}\n")
|
687
|
+
except (OSError, FileNotFoundError, PermissionError):
|
688
|
+
pass
|
689
|
+
|
690
|
+
busy.end()
|
691
|
+
# t1 = perf_counter()
|
692
|
+
# print (f"Ready, took {t1 - t0:.2f}sec")
|
693
|
+
return self._available_fonts
|
694
|
+
|
695
|
+
|
696
|
+
def plugin(kernel, lifecycle):
|
697
|
+
if lifecycle == "register":
|
698
|
+
_ = kernel.translation
|
699
|
+
context = kernel.root
|
700
|
+
# Generate setting for system-directories
|
701
|
+
directories = []
|
702
|
+
systype = platform.system()
|
703
|
+
if systype == "Windows":
|
704
|
+
if "WINDIR" in os.environ:
|
705
|
+
windir = os.environ["WINDIR"]
|
706
|
+
else:
|
707
|
+
windir = "c:\\windows"
|
708
|
+
directories.append(join(windir, "Fonts"))
|
709
|
+
if "LOCALAPPDATA" in os.environ:
|
710
|
+
appdir = os.environ["LOCALAPPDATA"]
|
711
|
+
directories.append(join(appdir, "Microsoft\\Windows\\Fonts"))
|
712
|
+
elif systype == "Linux":
|
713
|
+
directories.append("/usr/share/fonts")
|
714
|
+
directories.append("/usr/local/share/fonts")
|
715
|
+
directories.append("~/.local/share/fonts")
|
716
|
+
elif systype == "Darwin":
|
717
|
+
directories.append("/System/Library/Fonts")
|
718
|
+
directories.append("/Library/Fonts")
|
719
|
+
directories.append("~/Library/Fonts")
|
720
|
+
choices = [
|
721
|
+
{
|
722
|
+
"attr": "system_font_directories",
|
723
|
+
"object": context,
|
724
|
+
"page": "_95_Fonts",
|
725
|
+
"section": "_95_System font locations",
|
726
|
+
"default": directories,
|
727
|
+
"type": list,
|
728
|
+
"columns": [
|
729
|
+
{
|
730
|
+
"attr": "directory",
|
731
|
+
"type": str,
|
732
|
+
"label": _("Directory"),
|
733
|
+
"width": -1,
|
734
|
+
"editable": True,
|
735
|
+
},
|
736
|
+
],
|
737
|
+
"label": "_00_",
|
738
|
+
"style": "chart",
|
739
|
+
"primary": "directory",
|
740
|
+
"allow_deletion": True,
|
741
|
+
"allow_duplication": True,
|
742
|
+
"tip": _("Places where MeerK40t will look for fonts."),
|
743
|
+
},
|
744
|
+
]
|
745
|
+
kernel.register_choices("preferences", choices)
|
746
|
+
context.fonts = Meerk40tFonts(context=context)
|
747
|
+
|
748
|
+
# Register update routine for linetext
|
749
|
+
kernel.register("path_updater/linetext", context.fonts.update)
|
750
|
+
for idx, attrib in enumerate(
|
751
|
+
("mkfontsize", "mkfontweld", "mkfontspacing", "mklinegap", "mkalign")
|
752
|
+
):
|
753
|
+
kernel.register(f"registered_mk_svg_parameters/font{idx}", attrib)
|
754
|
+
|
755
|
+
|
756
|
+
@context.console_argument("x", type=Length, help=_("X-Coordinate"))
|
757
|
+
@context.console_argument("y", type=Length, help=_("Y-Coordinate"))
|
758
|
+
@context.console_argument("text", type=str, help=_("Text to render"))
|
759
|
+
@context.console_option("font", "f", type=str, help=_("Font file."))
|
760
|
+
@context.console_option(
|
761
|
+
"font_size", "s", type=Length, default="20px", help=_("Font size")
|
762
|
+
)
|
763
|
+
@context.console_option(
|
764
|
+
"font_spacing",
|
765
|
+
"g",
|
766
|
+
type=float,
|
767
|
+
default=1,
|
768
|
+
help=_("Character spacing factor"),
|
769
|
+
)
|
770
|
+
@context.console_command(
|
771
|
+
"linetext", help=_("linetext <font> <font_size> <text>")
|
772
|
+
)
|
773
|
+
def linetext(
|
774
|
+
command,
|
775
|
+
channel,
|
776
|
+
_,
|
777
|
+
x=None,
|
778
|
+
y=None,
|
779
|
+
text=None,
|
780
|
+
font=None,
|
781
|
+
font_size=None,
|
782
|
+
font_spacing=None,
|
783
|
+
remainder=None,
|
784
|
+
**kwargs,
|
785
|
+
):
|
786
|
+
if x is None or y is None or text is None:
|
787
|
+
channel(_("linetext <x> <y> <text> - please provide all required arguments"))
|
788
|
+
registered_fonts = context.fonts.available_fonts()
|
789
|
+
for item in registered_fonts:
|
790
|
+
channel(f"{item[1]} ({item[0]})")
|
791
|
+
return
|
792
|
+
try:
|
793
|
+
x = float(Length(x))
|
794
|
+
y = float(Length(y))
|
795
|
+
except ValueError:
|
796
|
+
channel(_("Invalid coordinates"))
|
797
|
+
return
|
798
|
+
if text is None or text == "":
|
799
|
+
channel(_("No text given."))
|
800
|
+
return
|
801
|
+
|
802
|
+
|
803
|
+
if font_spacing is None:
|
804
|
+
font_spacing = 1
|
805
|
+
|
806
|
+
context.setting(str, "last_font", None)
|
807
|
+
if font is None:
|
808
|
+
font = context.last_font
|
809
|
+
|
810
|
+
font_name, font_path = context.fonts.retrieve_font(font)
|
811
|
+
if font_name is None:
|
812
|
+
channel(f"Could not find a valid font file for '{font}'")
|
813
|
+
registered_fonts = context.fonts.available_fonts()
|
814
|
+
for item in registered_fonts:
|
815
|
+
channel(f"{item[1]} ({item[0]})")
|
816
|
+
return
|
817
|
+
|
818
|
+
channel(f"Will use font '{font_name}' ({font_path})")
|
819
|
+
|
820
|
+
path_node = context.fonts.create_linetext_node(
|
821
|
+
x, y, text, font_path, font_size, font_spacing
|
822
|
+
)
|
823
|
+
# path_node = PathNode(
|
824
|
+
# path=path.path,
|
825
|
+
# matrix=Matrix.translate(0, float(font_size)),
|
826
|
+
# stroke=Color("black"),
|
827
|
+
# )
|
828
|
+
context.elements.elem_branch.add_node(path_node)
|
829
|
+
if context.elements.classify_new:
|
830
|
+
context.elements.classify([path_node])
|
831
|
+
context.elements.set_emphasis([path_node])
|
832
|
+
|
833
|
+
context.signal("element_added", path_node)
|
834
|
+
context.signal("refresh_scene", "Scene")
|