fritzkit 0.0.0-stage → 0.1.0
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.
- package/.claude-plugin/marketplace.json +12 -0
- package/.claude-plugin/plugin.json +13 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +429 -2
- package/dist/cli/commands/doctor.d.ts +4 -0
- package/dist/cli/commands/doctor.js +7 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/export.d.ts +17 -0
- package/dist/cli/commands/export.js +81 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/kicad.d.ts +10 -0
- package/dist/cli/commands/kicad.js +22 -0
- package/dist/cli/commands/kicad.js.map +1 -0
- package/dist/cli/commands/parts.d.ts +22 -0
- package/dist/cli/commands/parts.js +46 -0
- package/dist/cli/commands/parts.js.map +1 -0
- package/dist/cli/commands/prune.d.ts +14 -0
- package/dist/cli/commands/prune.js +67 -0
- package/dist/cli/commands/prune.js.map +1 -0
- package/dist/cli/commands/render.d.ts +24 -0
- package/dist/cli/commands/render.js +62 -0
- package/dist/cli/commands/render.js.map +1 -0
- package/dist/cli/commands/schema.d.ts +10 -0
- package/dist/cli/commands/schema.js +35 -0
- package/dist/cli/commands/schema.js.map +1 -0
- package/dist/cli/commands/ui.d.ts +14 -0
- package/dist/cli/commands/ui.js +67 -0
- package/dist/cli/commands/ui.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +24 -0
- package/dist/cli/commands/validate.js +91 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/context.d.ts +81 -0
- package/dist/cli/context.js +139 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +8 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/program.d.ts +21 -0
- package/dist/cli/program.js +158 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/raster.d.ts +4 -0
- package/dist/cli/raster.js +9 -0
- package/dist/cli/raster.js.map +1 -0
- package/dist/core/compile/breadboard-util.d.ts +12 -0
- package/dist/core/compile/breadboard-util.js +107 -0
- package/dist/core/compile/breadboard-util.js.map +1 -0
- package/dist/core/compile/breadboard.d.ts +60 -0
- package/dist/core/compile/breadboard.js +513 -0
- package/dist/core/compile/breadboard.js.map +1 -0
- package/dist/core/compile/fzp.d.ts +12 -0
- package/dist/core/compile/fzp.js +183 -0
- package/dist/core/compile/fzp.js.map +1 -0
- package/dist/core/compile/icon.d.ts +17 -0
- package/dist/core/compile/icon.js +27 -0
- package/dist/core/compile/icon.js.map +1 -0
- package/dist/core/compile/index.d.ts +4 -0
- package/dist/core/compile/index.js +62 -0
- package/dist/core/compile/index.js.map +1 -0
- package/dist/core/compile/pcb.d.ts +39 -0
- package/dist/core/compile/pcb.js +164 -0
- package/dist/core/compile/pcb.js.map +1 -0
- package/dist/core/compile/schematic.d.ts +69 -0
- package/dist/core/compile/schematic.js +240 -0
- package/dist/core/compile/schematic.js.map +1 -0
- package/dist/core/compile/style.d.ts +52 -0
- package/dist/core/compile/style.js +72 -0
- package/dist/core/compile/style.js.map +1 -0
- package/dist/core/compile/types.d.ts +50 -0
- package/dist/core/compile/types.js +15 -0
- package/dist/core/compile/types.js.map +1 -0
- package/dist/core/export/bin.d.ts +61 -0
- package/dist/core/export/bin.js +221 -0
- package/dist/core/export/bin.js.map +1 -0
- package/dist/core/export/fzpz.d.ts +37 -0
- package/dist/core/export/fzpz.js +121 -0
- package/dist/core/export/fzpz.js.map +1 -0
- package/dist/core/export/index.d.ts +5 -0
- package/dist/core/export/index.js +6 -0
- package/dist/core/export/index.js.map +1 -0
- package/dist/core/export/install.d.ts +77 -0
- package/dist/core/export/install.js +183 -0
- package/dist/core/export/install.js.map +1 -0
- package/dist/core/export/prune.d.ts +72 -0
- package/dist/core/export/prune.js +165 -0
- package/dist/core/export/prune.js.map +1 -0
- package/dist/core/fritzing.d.ts +22 -0
- package/dist/core/fritzing.js +77 -0
- package/dist/core/fritzing.js.map +1 -0
- package/dist/core/identity.d.ts +29 -0
- package/dist/core/identity.js +70 -0
- package/dist/core/identity.js.map +1 -0
- package/dist/core/image/bbox.d.ts +27 -0
- package/dist/core/image/bbox.js +24 -0
- package/dist/core/image/bbox.js.map +1 -0
- package/dist/core/image/index.d.ts +8 -0
- package/dist/core/image/index.js +9 -0
- package/dist/core/image/index.js.map +1 -0
- package/dist/core/image/key.d.ts +72 -0
- package/dist/core/image/key.js +317 -0
- package/dist/core/image/key.js.map +1 -0
- package/dist/core/image/prepare.d.ts +31 -0
- package/dist/core/image/prepare.js +98 -0
- package/dist/core/image/prepare.js.map +1 -0
- package/dist/core/image/remover.d.ts +17 -0
- package/dist/core/image/remover.js +51 -0
- package/dist/core/image/remover.js.map +1 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.js +17 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/kicad/footprint.d.ts +104 -0
- package/dist/core/kicad/footprint.js +317 -0
- package/dist/core/kicad/footprint.js.map +1 -0
- package/dist/core/kicad/index.d.ts +25 -0
- package/dist/core/kicad/index.js +197 -0
- package/dist/core/kicad/index.js.map +1 -0
- package/dist/core/kicad/sexpr.d.ts +35 -0
- package/dist/core/kicad/sexpr.js +163 -0
- package/dist/core/kicad/sexpr.js.map +1 -0
- package/dist/core/resolve.d.ts +113 -0
- package/dist/core/resolve.js +301 -0
- package/dist/core/resolve.js.map +1 -0
- package/dist/core/schema/part.d.ts +995 -0
- package/dist/core/schema/part.js +278 -0
- package/dist/core/schema/part.js.map +1 -0
- package/dist/core/schema/part.schema.json +1122 -0
- package/dist/core/svg.d.ts +20 -0
- package/dist/core/svg.js +56 -0
- package/dist/core/svg.js.map +1 -0
- package/dist/core/units.d.ts +28 -0
- package/dist/core/units.js +42 -0
- package/dist/core/units.js.map +1 -0
- package/dist/core/validate/checker.d.ts +46 -0
- package/dist/core/validate/checker.js +142 -0
- package/dist/core/validate/checker.js.map +1 -0
- package/dist/core/validate/doctor.d.ts +25 -0
- package/dist/core/validate/doctor.js +156 -0
- package/dist/core/validate/doctor.js.map +1 -0
- package/dist/core/validate/explain.d.ts +10 -0
- package/dist/core/validate/explain.js +135 -0
- package/dist/core/validate/explain.js.map +1 -0
- package/dist/core/validate/index.d.ts +21 -0
- package/dist/core/validate/index.js +96 -0
- package/dist/core/validate/index.js.map +1 -0
- package/dist/core/validate/structural.d.ts +74 -0
- package/dist/core/validate/structural.js +664 -0
- package/dist/core/validate/structural.js.map +1 -0
- package/dist/core/workspace.d.ts +69 -0
- package/dist/core/workspace.js +213 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/mcp/jsonpatch.d.ts +30 -0
- package/dist/mcp/jsonpatch.js +145 -0
- package/dist/mcp/jsonpatch.js.map +1 -0
- package/dist/mcp/main.d.ts +2 -0
- package/dist/mcp/main.js +5 -0
- package/dist/mcp/main.js.map +1 -0
- package/dist/mcp/server.d.ts +28 -0
- package/dist/mcp/server.js +310 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/server/api.d.ts +72 -0
- package/dist/server/api.js +507 -0
- package/dist/server/api.js.map +1 -0
- package/dist/server/events.d.ts +28 -0
- package/dist/server/events.js +134 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/http.d.ts +36 -0
- package/dist/server/http.js +127 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/index.d.ts +65 -0
- package/dist/server/index.js +193 -0
- package/dist/server/index.js.map +1 -0
- package/dist/ui/assets/index-DLU8opyI.js +63 -0
- package/dist/ui/assets/index-GhUA-T3b.css +1 -0
- package/dist/ui/index.html +14 -0
- package/package.json +89 -4
- package/skills/fritzkit/SKILL.md +690 -0
- package/skills/fritzkit/reference/examples.md +275 -0
- package/skills/fritzkit/reference/part-json.md +333 -0
- package/skills/fritzkit/scripts/measure.py +197 -0
- package/tools/fzp-checker/.python-version +1 -0
- package/tools/fzp-checker/README.md +45 -0
- package/tools/fzp-checker/check_fzp.py +48 -0
- package/tools/fzp-checker/pyproject.toml +26 -0
- package/tools/fzp-checker/uv.lock +266 -0
- package/tools/fzp-checker/vendor/LICENSE.txt +8 -0
- package/tools/fzp-checker/vendor/UPSTREAM.md +25 -0
- package/tools/fzp-checker/vendor/fzp_checker.py +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/__init__.py +26 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/check_missing_leg_ids.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/connector_numbering.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_checker_runner.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_checkers.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_svg_checkers.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/fzp_utils.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/svg_checkers.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/__pycache__/svg_utils.cpython-313.pyc +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/check_missing_leg_ids.py +44 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkascii.py +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkcase.py +156 -0
- package/tools/fzp-checker/vendor/scripts/checks/checkcopies.py +104 -0
- package/tools/fzp-checker/vendor/scripts/checks/connector_numbering.py +79 -0
- package/tools/fzp-checker/vendor/scripts/checks/connectors_misnumbered.py +228 -0
- package/tools/fzp-checker/vendor/scripts/checks/explain_errors.md +95 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_checker_runner.py +717 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_checkers.py +1069 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_svg_checkers.py +62 -0
- package/tools/fzp-checker/vendor/scripts/checks/fzp_utils.py +222 -0
- package/tools/fzp-checker/vendor/scripts/checks/inspector_fc51.png +0 -0
- package/tools/fzp-checker/vendor/scripts/checks/invisibleconnectors.py +118 -0
- package/tools/fzp-checker/vendor/scripts/checks/nolayeridcheck.txt +758 -0
- package/tools/fzp-checker/vendor/scripts/checks/requirements.txt +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/svg_checkers.py +953 -0
- package/tools/fzp-checker/vendor/scripts/checks/svg_utils.py +96 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_checkers.py +839 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/author_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_id.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_missing_node_members.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/buses_present_valid.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_missing_attributes.fzp.test +16 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_layers_present_valid.fzp.test +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_invalid.fzp.test +70 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_numbering_valid.fzp.test +70 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/connector_refs_valid.fzp.test +53 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_mixed.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_silkscreen_nested.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_style_attr.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_white_stroke.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_invalid_wrong_color.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_inherited.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_legacy.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_nested_copper.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_none.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_standard.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/copper_color_valid_uppercase.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/css_connector.fzp.test +100 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/description_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/duplicate_ids.fzp.test +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_missing.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/family_property_present_valid.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/font_size.fzp.test +46 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_invalid_format.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_missing.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/fritzing_version_present_valid.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_absent.fzp.test +15 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/gorn_present.fzp.test +15 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/hybrid_connectors.fzp.test +232 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/invalid_xml.fzp.test +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_match.fzp.test +29 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_ids_mismatch.fzp.test +30 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_invalid.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/layer_nesting_valid.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/matrix_transform.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_connector_refs.fzp.test +63 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/missing_leg_ids.fzp.test +34 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_missing.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_non_alnum.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_absent.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_special_chars_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/module_id_too_short.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_invalid.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_namedview.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/no_layer_valid.fzp.test +19 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/pcb_only.fzp.test +48 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_name.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_missing_value.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/property_fields_present.fzp.test +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/required_tags_present.fzp.test +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/stroke_test.fzp.test +60 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/template_svgs.fzp.test +35 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/title_present.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_ids.fzp.test +27 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_duplicates.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/unique_property_names_unique.fzp.test +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/valid_xml.fzp.test +249 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_invalid_format.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/version_present_valid.fzp.test +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_missing.fzp.test +3 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/core/views_present.fzp.test +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/connector_refs_valid_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/duplicate_ids_breadboard.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_absent_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/gorn_present_breadboard.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/hybrid_connectors_breadboard.svg +102 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_invalid.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/layer_nesting_valid.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/matrix_transform.svg +11 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_connector_refs_breadboard.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/missing_leg_ids_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_match_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/test_layer_ids_mismatch_breadboard.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/breadboard/unique_ids_breadboard.svg +13 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/hybrid_connectors_icon.svg +78 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_invalid.svg +4 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_namedview.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/no_layer_valid.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/icon/plain_pcb.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/connector_refs_valid_pcb.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_mixed.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_silkscreen_nested.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_style_attr.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_white_stroke.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_invalid_wrong_color.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_inherited.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_legacy.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_nested_copper.svg +9 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_none.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_standard.svg +10 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/copper_color_valid_uppercase.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/css_connector.svg +25 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/font_size.svg +54 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/hybrid_connectors_pcb.svg +35 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/missing_connector_refs_pcb.svg +14 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/pcb-arduino-shield.svg +25 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/stroke_test.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_match_pcb.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/pcb/test_layer_ids_mismatch_pcb.svg +12 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/connector_refs_valid_schematic.svg +7 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/hybrid_connectors_schematic.svg +59 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/missing_connector_refs_schematic.svg +8 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_match_schematic.svg +6 -0
- package/tools/fzp-checker/vendor/scripts/checks/test_data/svg/core/schematic/test_layer_ids_mismatch_schematic.svg +6 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from .fzp_checkers import FZPChecker
|
|
2
|
+
from .fzp_utils import FZPUtils
|
|
3
|
+
from lxml import etree
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
class FZPMissingConnectorRefsChecker(FZPChecker):
|
|
7
|
+
def __init__(self, fzp_doc, svg_docs):
|
|
8
|
+
super().__init__(fzp_doc)
|
|
9
|
+
self.svg_docs = svg_docs
|
|
10
|
+
|
|
11
|
+
def check(self):
|
|
12
|
+
view_layers = {
|
|
13
|
+
'pcbView': ['copper0', 'copper1'],
|
|
14
|
+
'breadboardView': ['breadboard'],
|
|
15
|
+
'schematicView': ['schematic']
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
for view_name, layers in view_layers.items():
|
|
19
|
+
svg_doc = self.svg_docs.get(view_name)
|
|
20
|
+
if not svg_doc:
|
|
21
|
+
continue
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
connector_layers = {}
|
|
25
|
+
connector_nodes = {} # Store the actual nodes
|
|
26
|
+
|
|
27
|
+
# Find connectors in each layer for this view
|
|
28
|
+
for layer in layers:
|
|
29
|
+
layer_groups = svg_doc.xpath(f"//*[@id='{layer}']")
|
|
30
|
+
for group in layer_groups:
|
|
31
|
+
connectors = group.xpath(".//*[starts-with(@id, 'connector') and (contains(@id, 'pin') or contains(@id, 'pad'))]")
|
|
32
|
+
for connector in connectors:
|
|
33
|
+
connector_id = connector.get('id')
|
|
34
|
+
if not connector_id:
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
if connector_id not in connector_layers:
|
|
38
|
+
connector_layers[connector_id] = set()
|
|
39
|
+
connector_nodes[connector_id] = connector # Store the node
|
|
40
|
+
connector_layers[connector_id].add(layer)
|
|
41
|
+
|
|
42
|
+
# Check FZP references for each connector's required layers
|
|
43
|
+
for connector_id, required_layers in connector_layers.items():
|
|
44
|
+
connector_num = connector_id.replace('connector', '').replace('pin', '').replace('pad', '')
|
|
45
|
+
connector_node = connector_nodes[connector_id]
|
|
46
|
+
for layer in required_layers:
|
|
47
|
+
refs = self.fzp_doc.xpath(f"//connector[@id='connector{connector_num}']/views/{view_name}/p[@layer='{layer}']")
|
|
48
|
+
if not refs:
|
|
49
|
+
self.add_error(f"Connector {connector_id} is in {layer} layer in SVG but not referenced in FZP {view_name}", node=connector_node)
|
|
50
|
+
|
|
51
|
+
except Exception as e:
|
|
52
|
+
self.add_error(f"Error processing {view_name} SVG: {str(e)}")
|
|
53
|
+
|
|
54
|
+
return self.get_result()
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def get_name():
|
|
58
|
+
return "missing_connector_refs"
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def get_description():
|
|
62
|
+
return "Check that all connectors in SVG layer groups are properly referenced in FZP"
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import zipfile
|
|
4
|
+
import tempfile
|
|
5
|
+
import shutil
|
|
6
|
+
|
|
7
|
+
class FZPUtils:
|
|
8
|
+
@staticmethod
|
|
9
|
+
def get_svg_path(fzp_path, image, view_name):
|
|
10
|
+
dir_path = os.path.dirname(fzp_path)
|
|
11
|
+
|
|
12
|
+
# Check if this is an extracted fzpz structure (SVGs in same directory as FZP)
|
|
13
|
+
is_fzpz_structure = FZPUtils.is_fzpz_structure(fzp_path, image)
|
|
14
|
+
|
|
15
|
+
if is_fzpz_structure:
|
|
16
|
+
# For fzpz files, check for the dot-prefixed naming conventions
|
|
17
|
+
if '/' in image:
|
|
18
|
+
# Try pattern: icon/file.svg -> icon.file.svg
|
|
19
|
+
fzpz_filename = image.replace('/', '.')
|
|
20
|
+
svg_path = os.path.join(dir_path, fzpz_filename)
|
|
21
|
+
if os.path.isfile(svg_path):
|
|
22
|
+
# Skip template detection for fzpz files - process all SVGs
|
|
23
|
+
return svg_path
|
|
24
|
+
|
|
25
|
+
# Try pattern: icon/file.svg -> svg.icon.file.svg
|
|
26
|
+
fzpz_filename = 'svg.' + image.replace('/', '.')
|
|
27
|
+
svg_path = os.path.join(dir_path, fzpz_filename)
|
|
28
|
+
if os.path.isfile(svg_path):
|
|
29
|
+
# Skip template detection for fzpz files - process all SVGs
|
|
30
|
+
return svg_path
|
|
31
|
+
|
|
32
|
+
# Fall back to checking basename only
|
|
33
|
+
image_filename = os.path.basename(image)
|
|
34
|
+
svg_path = os.path.join(dir_path, image_filename)
|
|
35
|
+
if os.path.isfile(svg_path):
|
|
36
|
+
# Skip template detection for fzpz files - process all SVGs
|
|
37
|
+
return svg_path
|
|
38
|
+
else:
|
|
39
|
+
# Standard fritzing-parts structure
|
|
40
|
+
up_one_level = os.path.dirname(dir_path)
|
|
41
|
+
|
|
42
|
+
# Try contrib first (for imported parts), then core
|
|
43
|
+
svg_path_contrib = os.path.join(up_one_level, 'svg', 'contrib', image)
|
|
44
|
+
if os.path.isfile(svg_path_contrib):
|
|
45
|
+
# Contrib parts cannot be templates, so return the path directly
|
|
46
|
+
return svg_path_contrib
|
|
47
|
+
else:
|
|
48
|
+
# Fall back to core directory
|
|
49
|
+
svg_path = os.path.join(up_one_level, 'svg', 'core', image)
|
|
50
|
+
# Only check templates for core parts
|
|
51
|
+
if FZPUtils.is_template(svg_path, view_name):
|
|
52
|
+
return None # Skip template SVGs
|
|
53
|
+
return svg_path
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def is_template(svg_path, view):
|
|
57
|
+
# Extract the filename from the svg_path
|
|
58
|
+
filename = os.path.basename(svg_path)
|
|
59
|
+
|
|
60
|
+
# Initialize the flag to False
|
|
61
|
+
starts_with_prefix = False
|
|
62
|
+
|
|
63
|
+
if view == 'breadboardView':
|
|
64
|
+
# Check if filename starts with 'generic_ic_' or matches the 'generic_female_pin_header_' pattern
|
|
65
|
+
if filename.startswith('generic_ic_'):
|
|
66
|
+
starts_with_prefix = True
|
|
67
|
+
else:
|
|
68
|
+
# Define regex pattern for 'generic_female_pin_header_' filenames
|
|
69
|
+
pattern = r'^generic_female_pin_header_\d+_100mil_bread\.svg$'
|
|
70
|
+
starts_with_prefix = bool(re.match(pattern, filename))
|
|
71
|
+
|
|
72
|
+
elif view == 'iconView':
|
|
73
|
+
# For iconView, the filename should still start with 'generic_ic_'
|
|
74
|
+
starts_with_prefix = filename.startswith('generic_ic_')
|
|
75
|
+
|
|
76
|
+
elif view == 'schematicView':
|
|
77
|
+
# For schematicView, the filename should start with 'generic_'
|
|
78
|
+
starts_with_prefix = filename.startswith('generic_')
|
|
79
|
+
|
|
80
|
+
elif view == 'pcbView':
|
|
81
|
+
# For pcbView, check if the filename matches the 'dip_' or 'jumper_' pattern
|
|
82
|
+
dip_pattern = r'^dip_\d+_\d+mil_pcb\.svg$'
|
|
83
|
+
jumper_pattern = r'^jumper_\d+_\d+mil_pcb\.svg$'
|
|
84
|
+
starts_with_prefix = bool(re.match(dip_pattern, filename) or re.match(jumper_pattern, filename))
|
|
85
|
+
|
|
86
|
+
# Define valid views
|
|
87
|
+
valid_views = ['breadboardView', 'iconView', 'schematicView', 'pcbView']
|
|
88
|
+
|
|
89
|
+
# Check if the view is valid and if the filename starts with the correct prefix or matches the pattern
|
|
90
|
+
valid_view = view in valid_views
|
|
91
|
+
return starts_with_prefix and valid_view
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def is_fzpz_structure(fzp_path, image):
|
|
95
|
+
"""
|
|
96
|
+
Check if this appears to be an extracted fzpz structure.
|
|
97
|
+
In fzpz files, SVGs are in the same directory as the FZP.
|
|
98
|
+
FZPZ files use a naming convention where subdirectory/file.svg becomes subdirectory.file.svg
|
|
99
|
+
"""
|
|
100
|
+
dir_path = os.path.dirname(fzp_path)
|
|
101
|
+
|
|
102
|
+
# First check if the image file exists as-is (just the basename)
|
|
103
|
+
image_filename = os.path.basename(image)
|
|
104
|
+
svg_in_same_dir = os.path.isfile(os.path.join(dir_path, image_filename))
|
|
105
|
+
|
|
106
|
+
# If not found, check for the fzpz naming conventions:
|
|
107
|
+
# 1. subdirectory/file.svg becomes subdirectory.file.svg
|
|
108
|
+
# 2. subdirectory/file.svg becomes svg.subdirectory.file.svg
|
|
109
|
+
if not svg_in_same_dir and '/' in image:
|
|
110
|
+
# Try pattern: icon/file.svg -> icon.file.svg
|
|
111
|
+
fzpz_filename = image.replace('/', '.')
|
|
112
|
+
svg_in_same_dir = os.path.isfile(os.path.join(dir_path, fzpz_filename))
|
|
113
|
+
|
|
114
|
+
# Try pattern: icon/file.svg -> svg.icon.file.svg
|
|
115
|
+
if not svg_in_same_dir:
|
|
116
|
+
fzpz_filename = 'svg.' + image.replace('/', '.')
|
|
117
|
+
svg_in_same_dir = os.path.isfile(os.path.join(dir_path, fzpz_filename))
|
|
118
|
+
|
|
119
|
+
# Also check if we don't have the standard fritzing-parts directory structure
|
|
120
|
+
up_one_level = os.path.dirname(dir_path)
|
|
121
|
+
svg_in_standard_location = os.path.isfile(os.path.join(up_one_level, 'svg', 'core', image))
|
|
122
|
+
|
|
123
|
+
return svg_in_same_dir and not svg_in_standard_location
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def extract_fzpz(fzpz_path, extract_to=None):
|
|
127
|
+
"""
|
|
128
|
+
Extract an fzpz file to a temporary or specified directory.
|
|
129
|
+
Returns the path to the extracted FZP file.
|
|
130
|
+
"""
|
|
131
|
+
if not fzpz_path.endswith('.fzpz'):
|
|
132
|
+
raise ValueError("File must have .fzpz extension")
|
|
133
|
+
|
|
134
|
+
if not os.path.isfile(fzpz_path):
|
|
135
|
+
raise FileNotFoundError(f"FZPZ file not found: {fzpz_path}")
|
|
136
|
+
|
|
137
|
+
if extract_to is None:
|
|
138
|
+
extract_to = tempfile.mkdtemp()
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
with zipfile.ZipFile(fzpz_path, 'r') as zip_file:
|
|
142
|
+
FZPUtils._validate_zip_paths(zip_file, fzpz_path)
|
|
143
|
+
|
|
144
|
+
zip_file.extractall(extract_to)
|
|
145
|
+
|
|
146
|
+
# Find the FZP file in the extracted contents
|
|
147
|
+
for filename in os.listdir(extract_to):
|
|
148
|
+
if filename.endswith('.fzp'):
|
|
149
|
+
return os.path.join(extract_to, filename)
|
|
150
|
+
|
|
151
|
+
raise ValueError("No FZP file found in FZPZ archive")
|
|
152
|
+
|
|
153
|
+
except zipfile.BadZipFile:
|
|
154
|
+
raise ValueError(f"Invalid FZPZ file: {fzpz_path}")
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def _validate_zip_paths(zip_file, fzpz_path):
|
|
158
|
+
"""
|
|
159
|
+
Validate all paths in the zip file to prevent directory traversal attacks.
|
|
160
|
+
Raises ValueError if any unsafe paths are found.
|
|
161
|
+
"""
|
|
162
|
+
unsafe_paths = []
|
|
163
|
+
|
|
164
|
+
for member in zip_file.infolist():
|
|
165
|
+
path = member.filename
|
|
166
|
+
|
|
167
|
+
# Check for absolute paths
|
|
168
|
+
if os.path.isabs(path):
|
|
169
|
+
unsafe_paths.append(f"Absolute path: {path}")
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
# Check for directory traversal patterns
|
|
173
|
+
if ".." in path:
|
|
174
|
+
unsafe_paths.append(f"Directory traversal: {path}")
|
|
175
|
+
continue
|
|
176
|
+
|
|
177
|
+
# Normalize the path and check if it tries to escape the extraction directory
|
|
178
|
+
normalized_path = os.path.normpath(path)
|
|
179
|
+
if normalized_path.startswith("..") or os.path.isabs(normalized_path):
|
|
180
|
+
unsafe_paths.append(f"Path escape attempt: {path}")
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
if unsafe_paths:
|
|
184
|
+
error_msg = f"Security violation in FZPZ file '{fzpz_path}': " + "; ".join(unsafe_paths)
|
|
185
|
+
raise ValueError(error_msg)
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def cleanup_extraction(extracted_dir):
|
|
189
|
+
"""
|
|
190
|
+
Clean up extracted fzpz contents.
|
|
191
|
+
"""
|
|
192
|
+
if os.path.exists(extracted_dir):
|
|
193
|
+
shutil.rmtree(extracted_dir)
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def get_svg_path_from_view(fzp_doc, fzp_path, view_name, layer=None):
|
|
197
|
+
views_section = fzp_doc.xpath("//views")[0]
|
|
198
|
+
for view in views_section:
|
|
199
|
+
if view.tag == view_name:
|
|
200
|
+
layers = view.xpath("layers")
|
|
201
|
+
if layers:
|
|
202
|
+
if layer:
|
|
203
|
+
layer_elements = layers[0].xpath("layer")
|
|
204
|
+
for layer_element in layer_elements:
|
|
205
|
+
if layer_element.attrib.get("layerId") == layer:
|
|
206
|
+
image = layers[0].attrib.get("image")
|
|
207
|
+
if image:
|
|
208
|
+
return FZPUtils.get_svg_path(fzp_path, image, view.tag)
|
|
209
|
+
else:
|
|
210
|
+
image = layers[0].attrib.get("image")
|
|
211
|
+
if image:
|
|
212
|
+
return FZPUtils.get_svg_path(fzp_path, image, view.tag)
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def is_hybrid_or_unknown_layer(p_element):
|
|
218
|
+
layer = p_element.attrib.get("layer")
|
|
219
|
+
is_hybrid = p_element.attrib.get("hybrid") == "yes"
|
|
220
|
+
if not is_hybrid and layer == "unknown":
|
|
221
|
+
print(f"Unknown layer for regular connector in {p_element}.")
|
|
222
|
+
return layer == "unknown" or is_hybrid
|
|
Binary file
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import getopt
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import os.path
|
|
5
|
+
import re
|
|
6
|
+
import xml.dom.minidom
|
|
7
|
+
import xml.dom
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def usage():
|
|
11
|
+
print("""
|
|
12
|
+
usage:
|
|
13
|
+
invisibleconnectors.py -d [svg folder]
|
|
14
|
+
invisibleconnectors.py -f [svg file]
|
|
15
|
+
Looks for connector-like svg elements with no fill or stroke.
|
|
16
|
+
Fritzing (at least version <= 1.0.3) can not colorize open connectors in red if the graphic doesn't have
|
|
17
|
+
a fill or stroke.
|
|
18
|
+
""")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def check_file(svgFilename):
|
|
22
|
+
try:
|
|
23
|
+
dom = xml.dom.minidom.parse(svgFilename)
|
|
24
|
+
except xml.parsers.expat.ExpatError as err:
|
|
25
|
+
print(str(err), svgFilename)
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
todo = [dom.documentElement]
|
|
29
|
+
while len(todo) > 0:
|
|
30
|
+
element = todo.pop(0)
|
|
31
|
+
for node in element.childNodes:
|
|
32
|
+
if node.nodeType == node.ELEMENT_NODE:
|
|
33
|
+
todo.append(node)
|
|
34
|
+
|
|
35
|
+
if not "connector" in element.getAttribute("id"):
|
|
36
|
+
continue
|
|
37
|
+
if "terminal" in element.getAttribute("id"):
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
hasVisibleChild = False
|
|
41
|
+
for child in element.childNodes:
|
|
42
|
+
if child.nodeType == child.ELEMENT_NODE:
|
|
43
|
+
fill = child.getAttribute("fill")
|
|
44
|
+
if fill and fill != "none":
|
|
45
|
+
hasVisibleChild = True
|
|
46
|
+
break
|
|
47
|
+
|
|
48
|
+
if hasVisibleChild:
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
stroke = element.getAttribute("stroke")
|
|
52
|
+
fill = element.getAttribute("fill")
|
|
53
|
+
strokewidth = element.getAttribute("stroke-width")
|
|
54
|
+
|
|
55
|
+
if len(stroke) == 0:
|
|
56
|
+
style = element.getAttribute("style")
|
|
57
|
+
if len(style) != 0:
|
|
58
|
+
style = style.replace(";", ":")
|
|
59
|
+
styles = style.split(":")
|
|
60
|
+
for index, name in enumerate(styles):
|
|
61
|
+
if name == "stroke":
|
|
62
|
+
stroke = styles[index + 1]
|
|
63
|
+
elif name == "stroke-width":
|
|
64
|
+
strokewidth = styles[index + 1]
|
|
65
|
+
elif name == "fill":
|
|
66
|
+
fill = styles[index + 1]
|
|
67
|
+
|
|
68
|
+
if len(fill) > 0 and fill != "none":
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
if len(strokewidth) > 0 and strokewidth != "0":
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
print("invisible connector", svgFilename, element.getAttribute("id"))
|
|
75
|
+
|
|
76
|
+
def main():
|
|
77
|
+
try:
|
|
78
|
+
opts, args = getopt.getopt(sys.argv[1:], "hd:f:", ["help", "directory", "file"])
|
|
79
|
+
except getopt.GetoptError as err:
|
|
80
|
+
# print help information and exit:
|
|
81
|
+
print(str(err)) # will print something like "option -a not recognized"
|
|
82
|
+
usage()
|
|
83
|
+
sys.exit(2)
|
|
84
|
+
|
|
85
|
+
dir = None
|
|
86
|
+
file = None
|
|
87
|
+
|
|
88
|
+
for o, a in opts:
|
|
89
|
+
# print o
|
|
90
|
+
# print a
|
|
91
|
+
if o in ("-d", "--directory"):
|
|
92
|
+
dir = a
|
|
93
|
+
elif o in ("-f", "--file"):
|
|
94
|
+
file = a
|
|
95
|
+
elif o in ("-h", "--help"):
|
|
96
|
+
usage()
|
|
97
|
+
sys.exit(2)
|
|
98
|
+
else:
|
|
99
|
+
assert False, "unhandled option"
|
|
100
|
+
|
|
101
|
+
if not dir and not file:
|
|
102
|
+
usage()
|
|
103
|
+
sys.exit(2)
|
|
104
|
+
|
|
105
|
+
if file:
|
|
106
|
+
check_file(file)
|
|
107
|
+
else:
|
|
108
|
+
for root, dirs, files in os.walk(dir, topdown=False):
|
|
109
|
+
for filename in files:
|
|
110
|
+
if not filename.endswith(".svg"):
|
|
111
|
+
continue
|
|
112
|
+
|
|
113
|
+
svgFilename = os.path.join(root, filename)
|
|
114
|
+
check_file(svgFilename)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
main()
|