diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7938765..f833627 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,73 +1,89 @@
name: build
on:
- push:
- paths-ignore:
- - "**.md"
- - "**.bbcode"
- - LICENSE
- branches: [main, dev]
- pull_request:
- paths-ignore:
- - "**.md"
- - "**.bbcode"
- - LICENSE
- branches: [main]
+ push:
+ paths-ignore:
+ - "**.md"
+ - "**.bbcode"
+ - LICENSE
+ branches: [main, dev]
+ pull_request:
+ paths-ignore:
+ - "**.md"
+ - "**.bbcode"
+ - LICENSE
+ branches: [main]
jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - name: Install build dependencies (apt)
- run: sudo apt install -y libx11-dev libxcursor-dev libpng-dev
- continue-on-error: false
- - name: Get yarn cache directory path
- id: yarn-cache-dir-path
- run: echo "::set-output name=dir::$(yarn cache dir)"
- - name: Caching yarn packages
- uses: actions/cache@v2
- id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
- with:
- path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
- key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- restore-keys: |
- ${{ runner.os }}-yarn-
- - name: Set Up NodeJS 12.x
- uses: actions/setup-node@v1
- with:
- node-version: "12.x"
- - name: Caching pip packages
- uses: actions/cache@v2
- id: pip-cache # use this to check for `cache-hit` (`steps.pip-cache.outputs.cache-hit != 'true'`)
- with:
- path: ~/.cache/pip
- key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }}
- restore-keys: |
- ${{ runner.os }}-pip-
- - name: Set up Python 3.8
- uses: actions/setup-python@v2
- with:
- python-version: "3.8"
- - name: Generating `GoogleDot` Cursor Theme
- run: make
- continue-on-error: false
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install build dependencies (apt)
+ run: sudo apt install -y libx11-dev libxcursor-dev libpng-dev
+ continue-on-error: false
+ - name: Get yarn cache directory path
+ id: yarn-cache-dir-path
+ run: echo "::set-output name=dir::$(yarn cache dir)"
+ - name: Caching yarn packages
+ uses: actions/cache@v2
+ id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
+ with:
+ path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
+ key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-yarn-
+ - name: Set Up NodeJS 12.x
+ uses: actions/setup-node@v1
+ with:
+ node-version: "12.x"
+ - name: Caching pip packages
+ uses: actions/cache@v2
+ id: pip-cache # use this to check for `cache-hit` (`steps.pip-cache.outputs.cache-hit != 'true'`)
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }}
+ restore-keys: |
+ ${{ runner.os }}-pip-
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: "3.8"
+ - name: Generating `GoogleDot` Cursor Theme
+ run: make
+ continue-on-error: false
- - name: Compressing UNIX theme
- run: tar -cvzf GoogleDot.tar.gz themes/GoogleDot
+ - name: Compressing UNIX theme
+ run: |
+ tar -cvzf GoogleDot-Blue.tar.gz themes/GoogleDot-Blue
+ tar -cvzf GoogleDot-Black.tar.gz themes/GoogleDot-Blue
- - name: Uploading `bitmaps` artifact
- uses: actions/upload-artifact@v2
- with:
- name: bitmaps
- path: bitmaps/*
- - name: Uploading `GoogleDot` UNIX Theme artifact
- uses: actions/upload-artifact@v2
- with:
- name: GoogleDot
- path: GoogleDot.tar.gz
- - name: Uploading `GoogleDot` Windows Theme artifact
- uses: actions/upload-artifact@v2
- with:
- name: GoogleDot_Windows
- path: themes/GoogleDot_Windows/*
+ - name: Uploading `bitmaps` artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: bitmaps
+ path: bitmaps/*
+
+ - name: Uploading `GoogleDot-Blue` UNIX Theme artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: GoogleDot-Blue
+ path: GoogleDot-Blue.tar.gz
+
+ - name: Uploading `GoogleDot-Black` UNIX Theme artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: GoogleDot-Black
+ path: GoogleDot-Black.tar.gz
+
+ - name: Uploading `GoogleDot-Blue` Windows Theme artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: GoogleDot-Blue-Windows
+ path: themes/GoogleDot-Blue-Windows/*
+
+ - name: Uploading `GoogleDot-Black` Windows Theme artifact
+ uses: actions/upload-artifact@v2
+ with:
+ name: GoogleDot-Black-Windows
+ path: themes/GoogleDot-Black-Windows/*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5054963..0b541a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,31 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [unreleased]
+### Added
+
+- GoogleDot Black version
+- separated 'core' module in bitmapper
+- Makefile updated for GoogleDot
+- GoogleDot Black CI supports
+
+### Changed
+
+- All `.svg` formated with `xmllint`
+- `.svg` colors changed to key colors (Green & Blue)
+- Multiple cursors supports in `builder`
+- Dynamic theme-name & comments inside `builder/build.py`
+
## [v1.0.1] - 17 Feb 2021
### Added
-- Figma file added inside **README.md**
-- Organized build & bitmaps source code
-- Add **Make** compatibility
-- Only build options added in `Makefile`
-- Relinked few svg files for Windows cursors purpose
-- New build docs
+- Figma file added inside **README.md**
+- Organized build & bitmaps source code
+- Add **Make** compatibility
+- Only build options added in `Makefile`
+- Relinked few svg files for Windows cursors purpose
+- New build docs
### Changed
-- Fixed some linting problems of builder using `pylint`
-- Reduced Package Size
-- Customize sizes from `make`
-- CI workflow `build` compatibility with `make` commands
+- Fixed some linting problems of builder using `pylint`
+- Reduced Package Size
+- Customize sizes from `make`
+- CI workflow `build` compatibility with `make` commands
## [v1.0.0] - 27 Oct 2020
### Added
-- Initial release 🎊
-- Logo and badges
-- CI/CD Pipelines
+- Initial release 🎊
+- Logo and badges
+- CI/CD Pipelines
[unreleased]: https://github.com/ful1e5/Google_Cursor/compare/v1.0.1...main
[v1.0.1]: https://github.com/ful1e5/Google_Cursor/compare/v1.0.0...v1.0.1
diff --git a/PLING.bbcode b/PLING.bbcode
index 777f068..a2b8bfc 100644
--- a/PLING.bbcode
+++ b/PLING.bbcode
@@ -3,26 +3,27 @@
[/b][i]Get latest build[/i] @[b][url=https://github.com/ful1e5/Google_Cursor/actions]GitHub Actions[/url][/b]
[i]Release Notification[/i] at [b][url=https://twitter.com/ful1e5]Twitter[/url][/b](@ful1e5)
+For [i]Customizing Size[/i] check [b][url=https://github.com/ful1e5/Google_Cursor#manual-build]README.md[/url][/b]
[b]Linux/X11 installation[/b]
Get the latest stable Linux release from the [b][url=https://www.pling.com/p/1215613#files-panel]Pling[/url][/b]. Unpack [b].tar.gz[/b] file and follow these [b]commands[/b].
[b]Install[/b]
[b]For all user:[/b]
-[code]sudo mv GoogleDot /usr/share/icons[/code]
+[code]sudo mv GoogleDot-* /usr/share/icons[/code]
[b]For local user:[/b]
-[code]mv GoogleDot ~/.icons[/code]
+[code]mv GoogleDot-* ~/.icons[/code]
[b]Uninstall[/b]
[b]From all user:[/b]
-[code]sudo rm -r /usr/share/icons/GoogleDot[/code]
+[code]sudo rm -r /usr/share/icons/GoogleDot-*[/code]
[b]From local user:[/b]
-[code]rm -r ~/.icons/GoogleDot[/code]
+[code]rm -r ~/.icons/GoogleDot-*[/code]
[b]Window installation[/b]
[list=1]
- [*]unzip [b]GoogleDot_Windows.zip[/b] file[/*]
- [*]Open [b]GoogleDot_Windows/[/b] in Explorer, and [b]right-click[/b] on [b]install.inf[/b].[/*]
+ [*]unzip [b].zip[/b] file[/*]
+ [*]Open [b]unziped directory[/b] in Explorer, and [b]right-click[/b] on [b]install.inf[/b].[/*]
[*]Click 'Install' from the context menu, and authorise the modifications to your system.[/*]
[*]Open [i]Control Panel > Personalisation and Appearance > Change mouse pointers[/i], and select [b]GoogleDot Cursors[/b].[/*]
[*]Click '[b]Apply[/b]'.[/*]
@@ -37,4 +38,4 @@ Get the latest stable Linux release from the [b][url=https://www.pling.com/p/121
[/list]
[b]License & Terms[/b]
-'[b]GoogleDot[/b]' Cursor Theme is available under the terms of the [b]GPL-3.0[/b] license.
\ No newline at end of file
+'[b]GoogleDot[/b]' Cursor Theme is available under the terms of the [b]GPL-3.0[/b] license.
diff --git a/README.md b/README.md
index 51e50c9..6dda419 100644
--- a/README.md
+++ b/README.md
@@ -106,9 +106,15 @@ Cursor theme inspired on **google material design** for `Windows` and `Linux` wi
-
+
- GoogleDot Cursors 🍭
+ Blue GoogleDot Cursors 🍭
+
+
+
+
+
+ BlackBlue GoogleDot Cursors 🍭
### Manual Install
@@ -120,16 +126,16 @@ Cursor theme inspired on **google material design** for `Windows` and `Linux` wi
tar -xvf GoogleDot.tar.gz
# For local users
-mv GoogleDot ~/.icons/
+mv GoogleDot-* ~/.icons/
# For all users
-sudo mv GoogleDot /usr/share/icons/
+sudo mv GoogleDot-* /usr/share/icons/
```
#### Windows
-1. unzip `GoogleDot_Windows.zip` file
-2. Open `GoogleDot_Windows/` in Explorer, and **right click** on `install.inf`.
+1. unzip `.zip` file
+2. Open unziped directory in Explorer, and **right click** on `install.inf`.
3. Click 'Install' from the context menu, and authorize the modifications to your system.
4. Open _Control Panel > Personalization and Appearance > Change mouse pointers_, and select **GoogleDot Cursors**.
5. Click '**Apply**'.
@@ -140,9 +146,9 @@ sudo mv GoogleDot /usr/share/icons/
## External Libraries
-- libxcursor
-- libx11
-- libpng (<=1.6)
+- libxcursor
+- libx11
+- libpng (<=1.6)
#### Install External Libraries
@@ -173,41 +179,41 @@ sudo dnf install libX11-devel libXcursor-devel libpng-devel
## Build Dependencies
-- [gcc](https://gcc.gnu.org/install/)
-- [make](https://www.gnu.org/software/make/)
-- [nodejs](https://nodejs.org/en/) (<=12.x.x)
-- [yarn](https://classic.yarnpkg.com/en/docs/install/)
-- [python](https://www.python.org/downloads/) (<=3.8)
-- [pip3](https://pip.pypa.io/en/stable/installing/)
+- [gcc](https://gcc.gnu.org/install/)
+- [make](https://www.gnu.org/software/make/)
+- [nodejs](https://nodejs.org/en/) (<=12.x.x)
+- [yarn](https://classic.yarnpkg.com/en/docs/install/)
+- [python](https://www.python.org/downloads/) (<=3.8)
+- [pip3](https://pip.pypa.io/en/stable/installing/)
### Node Packages
-- [puppeteer](https://www.npmjs.com/package/puppeteer)
-- [pngjs](https://www.npmjs.com/package/pngjs)
-- [pixelmatch](https://www.npmjs.com/package/pixelmatch)
+- [puppeteer](https://www.npmjs.com/package/puppeteer)
+- [pngjs](https://www.npmjs.com/package/pngjs)
+- [pixelmatch](https://www.npmjs.com/package/pixelmatch)
### PyPi Packages
-- [clickgen](https://pypi.org/project/clickgen/s)
+- [clickgen](https://pypi.org/project/clickgen/s)
## Build Dependencies
-- [gcc](https://gcc.gnu.org/install/)
-- [make](https://www.gnu.org/software/make/)
-- [nodejs](https://nodejs.org/en/) (<=12.x.x)
-- [yarn](https://classic.yarnpkg.com/en/docs/install/)
-- [python](https://www.python.org/downloads/) (<=3.8)
-- [pip3](https://pip.pypa.io/en/stable/installing/)
+- [gcc](https://gcc.gnu.org/install/)
+- [make](https://www.gnu.org/software/make/)
+- [nodejs](https://nodejs.org/en/) (<=12.x.x)
+- [yarn](https://classic.yarnpkg.com/en/docs/install/)
+- [python](https://www.python.org/downloads/) (<=3.8)
+- [pip3](https://pip.pypa.io/en/stable/installing/)
### Node Packages
-- [puppeteer](https://www.npmjs.com/package/puppeteer)
-- [pngjs](https://www.npmjs.com/package/pngjs)
-- [pixelmatch](https://www.npmjs.com/package/pixelmatch)
+- [puppeteer](https://www.npmjs.com/package/puppeteer)
+- [pngjs](https://www.npmjs.com/package/pngjs)
+- [pixelmatch](https://www.npmjs.com/package/pixelmatch)
### PyPi Packages
-- [clickgen](https://pypi.org/project/clickgen/s)
+- [clickgen](https://pypi.org/project/clickgen/s)
## Build From Scratch
diff --git a/bitmapper/package.json b/bitmapper/package.json
index ea0355d..6df6d10 100644
--- a/bitmapper/package.json
+++ b/bitmapper/package.json
@@ -1,6 +1,6 @@
{
"name": "google_cursor",
- "version": "1.0.1",
+ "version": "1.1.0",
"description": "🍭 Cursor theme inspired on Google",
"main": "index.js",
"repository": "git@github.com:ful1e5/Google_Cursor.git",
diff --git a/bitmapper/src/config.ts b/bitmapper/src/config.ts
index 6ab4744..e56a352 100644
--- a/bitmapper/src/config.ts
+++ b/bitmapper/src/config.ts
@@ -1,23 +1,29 @@
-import path from "path";
-import { readdirSync, existsSync } from "fs";
+import { Colors } from "./core/types";
-// Directory resolve
-const projectRoot = path.resolve(__dirname, "../../");
-
-const outDir = path.resolve(projectRoot, "bitmaps");
-const staticSvgDir = path.resolve(projectRoot, "svg", "static");
-const animatedSvgDir = path.resolve(projectRoot, "svg", "animated");
-
-// Generate a svg list
-if (!existsSync(staticSvgDir) || !existsSync(animatedSvgDir)) {
- throw new Error("svg directory not found");
+interface Config {
+ themeName: string;
+ color: Colors;
}
-const staticCursors = readdirSync(staticSvgDir).map((f) =>
- path.resolve(staticSvgDir, f)
-);
-const animatedCursors = readdirSync(animatedSvgDir).map((f) =>
- path.resolve(animatedSvgDir, f)
-);
+const black = "#000000";
+const blue = "#4285F4";
+const white = "#FFFFFF";
+
+const config: Config[] = [
+ {
+ themeName: "GoogleDot-Blue",
+ color: {
+ base: blue,
+ outline: white,
+ },
+ },
+ {
+ themeName: "GoogleDot-Black",
+ color: {
+ base: black,
+ outline: white,
+ },
+ },
+];
-export { staticCursors, animatedCursors, outDir };
+export { config };
diff --git a/bitmapper/src/core/BitmapsGenerator.ts b/bitmapper/src/core/BitmapsGenerator.ts
new file mode 100644
index 0000000..7a017ba
--- /dev/null
+++ b/bitmapper/src/core/BitmapsGenerator.ts
@@ -0,0 +1,155 @@
+import fs from "fs";
+import path from "path";
+
+import puppeteer, { Browser, ElementHandle, Page } from "puppeteer";
+
+import { frameNumber } from "./util/frameNumber";
+import { matchImages } from "./util/matchImages";
+import { toHTML } from "./util/toHTML";
+
+class BitmapsGenerator {
+ /**
+ * Generate Png files from svg code.
+ * @param themeName Give name, So all bitmaps files are organized in one directory.
+ * @param bitmapsDir `absolute` or `relative` path, Where `.png` files will store.
+ */
+ constructor(private bitmapsDir: string) {
+ this.bitmapsDir = path.resolve(bitmapsDir);
+ this.createDir(this.bitmapsDir);
+ }
+
+ /**
+ * Create directory if it doesn't exists.
+ * @param dirPath directory `absolute` path.
+ */
+ private createDir(dirPath: string) {
+ if (!fs.existsSync(dirPath)) {
+ fs.mkdirSync(dirPath, { recursive: true });
+ }
+ }
+
+ /**
+ * Prepare headless browser.
+ */
+ public async getBrowser(): Promise {
+ return await puppeteer.launch({
+ ignoreDefaultArgs: ["--no-sandbox"],
+ headless: true,
+ });
+ }
+
+ private async getSvgElement(
+ page: Page,
+ content: string
+ ): Promise> {
+ if (!content) {
+ throw new Error(`${content} File Read error`);
+ }
+
+ const html = toHTML(content);
+ await page.setContent(html, { timeout: 0 });
+
+ const svg = await page.$("#container svg");
+
+ if (!svg) {
+ throw new Error("svg element not found!");
+ }
+ return svg;
+ }
+
+ public async generateStatic(browser: Browser, content: string, key: string) {
+ const page = await browser.newPage();
+ const svg = await this.getSvgElement(page, content);
+
+ const out = path.resolve(this.bitmapsDir, `${key}.png`);
+
+ await svg.screenshot({ omitBackground: true, path: out });
+ await page.close();
+ }
+
+ private async screenshot(
+ element: ElementHandle
+ ): Promise {
+ const buffer = await element.screenshot({
+ encoding: "binary",
+ omitBackground: true,
+ });
+
+ if (!buffer) {
+ throw new Error("SVG element screenshot not working");
+ }
+ return buffer;
+ }
+
+ private async stopAnimation(page: Page) {
+ const client = await page.target().createCDPSession();
+ await client.send("Animation.setPlaybackRate", {
+ playbackRate: 0,
+ });
+ }
+
+ private async resumeAnimation(page: Page, playbackRate: number) {
+ const client = await page.target().createCDPSession();
+ await client.send("Animation.setPlaybackRate", {
+ playbackRate,
+ });
+ }
+
+ private async saveFrameImage(key: string, frame: Buffer | string) {
+ const out_path = path.resolve(this.bitmapsDir, key);
+ fs.writeFileSync(out_path, frame);
+ }
+
+ public async generateAnimated(
+ browser: Browser,
+ content: string,
+ key: string,
+ options?: {
+ playbackRate?: number;
+ diff?: number;
+ frameLimit?: number;
+ framePadding?: number;
+ }
+ ) {
+ const opt = Object.assign(
+ { playbackRate: 0.1, diff: 0, frameLimit: 300, framePadding: 4 },
+ options
+ );
+
+ const page = await browser.newPage();
+ const svg = await this.getSvgElement(page, content);
+ await this.stopAnimation(page);
+
+ let index = 1;
+ let breakRendering = false;
+ let prevImg: Buffer | string;
+
+ // Rendering frames till `imgN` matched to `imgN-1` (When Animation is done)
+ while (!breakRendering) {
+ if (index > opt.frameLimit) {
+ throw new Error("Reached the frame limit.");
+ }
+
+ await this.resumeAnimation(page, opt.playbackRate);
+ const img: string | Buffer = await this.screenshot(svg);
+ await this.stopAnimation(page);
+
+ if (index > 1) {
+ // @ts-ignore
+ const diff = matchImages(prevImg, img);
+ if (diff <= opt.diff) {
+ breakRendering = !breakRendering;
+ }
+ }
+ const number = frameNumber(index, opt.framePadding);
+ const frame = `${key}-${number}.png`;
+
+ this.saveFrameImage(frame, img);
+
+ prevImg = img;
+ ++index;
+ }
+ await page.close();
+ }
+}
+export { BitmapsGenerator };
diff --git a/bitmapper/src/core/SVGHandler/SvgDirectoryParser.ts b/bitmapper/src/core/SVGHandler/SvgDirectoryParser.ts
new file mode 100644
index 0000000..65756da
--- /dev/null
+++ b/bitmapper/src/core/SVGHandler/SvgDirectoryParser.ts
@@ -0,0 +1,77 @@
+import fs from "fs";
+import path from "path";
+
+interface Svg {
+ key: string;
+ content: string;
+}
+
+class SvgDirectoryParser {
+ /**
+ * Manage and Parse SVG file path in `absolute` fashion.
+ * This Parser look svg files as below fashion:
+ * `
+ * <@svgDir>/static
+ * <@svgDir>/animated
+ * `
+ * @param svgDir is relative/absolute path, Where `SVG` files are stored.
+ */
+ semiAnimated: boolean = false;
+ constructor(private svgDir: string) {
+ if (!fs.existsSync(this.svgDir)) {
+ throw new Error(`SVG files not found in ${this.svgDir}`);
+ }
+ }
+
+ private readData(f: string): Svg {
+ const content = fs.readFileSync(f, "utf-8");
+ const key = path.basename(f, ".svg");
+ return { content, key };
+ }
+
+ /**
+ * Return absolute paths array of SVG files data located inside '@svgDir/static'
+ */
+ public getStatic(): Svg[] {
+ const staticDir = path.resolve(this.svgDir, "static");
+
+ if (!fs.existsSync(staticDir)) {
+ console.log(`${this.svgDir} contains semi-animated .svg files`);
+ this.semiAnimated = true;
+ return [];
+ } else {
+ const svgs = fs
+ .readdirSync(staticDir)
+ .map((f) => this.readData(path.resolve(staticDir, f)));
+
+ if (svgs.length == 0) {
+ throw new Error("Static Cursors directory is empty");
+ }
+ return svgs;
+ }
+ }
+ /**
+ * Return absolute paths array of SVG files data located inside '@svgDir/animated'
+ */
+ public getAnimated(): Svg[] {
+ const animatedDir = path.resolve(this.svgDir, "animated");
+
+ if (!fs.existsSync(animatedDir)) {
+ throw new Error("Animated Cursors directory not found");
+ }
+
+ const svgs = fs
+ .readdirSync(animatedDir)
+ .map((f) => this.readData(path.resolve(animatedDir, f)));
+
+ if (svgs.length == 0 && this.semiAnimated) {
+ throw new Error(
+ `Can't parse svg directory ${this.svgDir} as semi-animated theme`
+ );
+ }
+
+ return svgs;
+ }
+}
+
+export { SvgDirectoryParser };
diff --git a/bitmapper/src/core/SVGHandler/colorSvg.ts b/bitmapper/src/core/SVGHandler/colorSvg.ts
new file mode 100644
index 0000000..148dcd2
--- /dev/null
+++ b/bitmapper/src/core/SVGHandler/colorSvg.ts
@@ -0,0 +1,52 @@
+import { Colors } from "../types";
+
+/**
+ * Default Key Colors for generating colored svg.
+ * base="#00FF00" (Green)
+ * outline="#0000FF" (Blue)
+ * watch.background="#FF0000" (Red)
+ * */
+const defaultKeyColors: Colors = {
+ base: "#00FF00",
+ outline: "#0000FF",
+ watch: {
+ background: "#FF0000",
+ },
+};
+
+/**
+ * Customize colors of svg code.
+ * @param {string} content SVG code.
+ * @param {Colors} colors Customize colors.
+ * @param {Colors} [keys] Colors Key, That was written SVG code.
+ * @returns {string} SVG code with colors.
+ */
+const colorSvg = (
+ content: string,
+ colors: Colors,
+ keys: Colors = defaultKeyColors
+): string => {
+ content = content
+ .replace(new RegExp(keys.base, "ig"), colors.base)
+ .replace(new RegExp(keys.outline, "ig"), colors.outline);
+
+ try {
+ // === trying to replace `watch` color ===
+
+ if (!colors.watch?.background) {
+ throw new Error("");
+ }
+ const { background: b } = colors.watch;
+ content = content.replace(new RegExp(keys.watch!.background, "ig"), b); // Watch Background
+ } catch (error) {
+ // === on error => replace `watch` color as `base` ===
+
+ content = content.replace(
+ new RegExp(keys.watch!.background, "ig"),
+ colors.base
+ );
+ }
+ return content;
+};
+
+export { colorSvg };
diff --git a/bitmapper/src/core/SVGHandler/index.ts b/bitmapper/src/core/SVGHandler/index.ts
new file mode 100644
index 0000000..308a433
--- /dev/null
+++ b/bitmapper/src/core/SVGHandler/index.ts
@@ -0,0 +1,4 @@
+import { colorSvg } from "./colorSvg";
+import { SvgDirectoryParser } from "./SvgDirectoryParser";
+
+export { colorSvg, SvgDirectoryParser };
diff --git a/bitmapper/src/core/index.ts b/bitmapper/src/core/index.ts
new file mode 100644
index 0000000..2fe958b
--- /dev/null
+++ b/bitmapper/src/core/index.ts
@@ -0,0 +1,4 @@
+import { BitmapsGenerator } from "./BitmapsGenerator";
+import * as SVGHandler from "./SVGHandler";
+
+export { BitmapsGenerator, SVGHandler };
diff --git a/bitmapper/src/core/types.ts b/bitmapper/src/core/types.ts
new file mode 100644
index 0000000..cd5ddb3
--- /dev/null
+++ b/bitmapper/src/core/types.ts
@@ -0,0 +1,20 @@
+/**
+ * Hex Colors in string Format.
+ *
+ * `Example: `"#FFFFFF"
+ */
+type HexColor = string;
+
+/**
+ * @Colors expect `base`, `outline` & `watch-background` colors in **HexColor** Format.
+ * @default background is `base` color.
+ */
+type Colors = {
+ base: HexColor;
+ outline: HexColor;
+ watch?: {
+ background: HexColor;
+ };
+};
+
+export { Colors };
diff --git a/bitmapper/src/utils/frameNumber.ts b/bitmapper/src/core/util/frameNumber.ts
similarity index 100%
rename from bitmapper/src/utils/frameNumber.ts
rename to bitmapper/src/core/util/frameNumber.ts
diff --git a/bitmapper/src/utils/matchImages.ts b/bitmapper/src/core/util/matchImages.ts
similarity index 94%
rename from bitmapper/src/utils/matchImages.ts
rename to bitmapper/src/core/util/matchImages.ts
index e1a2844..625d2bd 100644
--- a/bitmapper/src/utils/matchImages.ts
+++ b/bitmapper/src/core/util/matchImages.ts
@@ -6,6 +6,6 @@ export const matchImages = (img1: Buffer, img2: Buffer): number => {
const { data: imgNData } = PNG.sync.read(img2);
return Pixelmatch(img1Data, imgNData, null, width, height, {
- threshold: 0.001,
+ threshold: 0.1,
});
};
diff --git a/bitmapper/src/utils/toHTML.ts b/bitmapper/src/core/util/toHTML.ts
similarity index 89%
rename from bitmapper/src/utils/toHTML.ts
rename to bitmapper/src/core/util/toHTML.ts
index 36fbbf4..aecb3d7 100644
--- a/bitmapper/src/utils/toHTML.ts
+++ b/bitmapper/src/core/util/toHTML.ts
@@ -16,4 +16,4 @@ export const template = `
`;
export const toHTML = (svgData: string): string =>
- template.replace("", svgData);
+ template.replace("", svgData);
diff --git a/bitmapper/src/index.ts b/bitmapper/src/index.ts
index d51f0f5..75dbfdc 100644
--- a/bitmapper/src/index.ts
+++ b/bitmapper/src/index.ts
@@ -1,127 +1,37 @@
-import fs from "fs";
import path from "path";
-import puppeteer, { ElementHandle, Page } from "puppeteer";
-import { animatedCursors, outDir, staticCursors } from "./config";
-import { frameNumber } from "./utils/frameNumber";
-import { matchImages } from "./utils/matchImages";
-import { toHTML } from "./utils/toHTML";
+import { BitmapsGenerator, SVGHandler } from "./core";
+import { config } from "./config";
-const getSVGElement = async (page: Page) => {
- const svg = await page.$("#container svg");
-
- if (!svg) {
- throw new Error("svg element not found!");
- }
- return svg;
-};
-
-const screenshot = async (element: ElementHandle): Promise => {
- return element.screenshot({
- omitBackground: true,
- encoding: "binary",
- });
-};
-
-const stopAnimation = async (page: Page) => {
- // @ts-ignore
- await page._client.send("Animation.setPlaybackRate", {
- playbackRate: 0,
- });
-};
-
-const resumeAnimation = async (page: Page, playbackRate: number = 0.1) => {
- // @ts-ignore
- await page._client.send("Animation.setPlaybackRate", {
- playbackRate,
- });
-};
-
-const saveFrameImage = (key: string, frame: Buffer) => {
- const out_path = path.resolve(outDir, key);
- fs.writeFileSync(out_path, frame, { encoding: "binary" });
-};
+const root = path.resolve(__dirname, "../../");
+const svgDir = path.resolve(root, "svg");
const main = async () => {
- const browser = await puppeteer.launch({
- ignoreDefaultArgs: ["--single-process", "--no-sandbox"],
- headless: true,
- });
+ for (const { themeName, color } of config) {
+ console.log("=>", themeName);
- if (!fs.existsSync(outDir)) {
- fs.mkdirSync(outDir);
- } else {
- throw new Error(`out directory '${outDir}' already exists.`);
- }
+ const bitmapsDir = path.resolve(root, "bitmaps", themeName);
+ const svg = new SVGHandler.SvgDirectoryParser(svgDir);
- for (const svgFilePath of staticCursors) {
- const svgData = fs.readFileSync(svgFilePath, "utf-8");
- if (!svgData) {
- throw new Error(`${svgFilePath} File Read error`);
- }
-
- const page = await browser.newPage();
- const html = toHTML(svgData);
-
- await page.setContent(html);
- const svg = await getSVGElement(page);
+ const png = new BitmapsGenerator(bitmapsDir);
+ const browser = await png.getBrowser();
- const key = `${path.basename(svgFilePath, ".svg")}.png`;
- const out = path.join(outDir, key);
+ for (let { key, content } of svg.getStatic()) {
+ console.log(" -> Saving", key, "...");
- console.log("Saving", key, "...");
- await svg.screenshot({ omitBackground: true, path: out });
- await page.close();
- }
-
- for (const svgFilePath of animatedCursors) {
- const svgData = fs.readFileSync(svgFilePath, "utf8");
- if (!svgData) {
- throw new Error(`${svgFilePath} File Read error`);
+ content = SVGHandler.colorSvg(content, color);
+ await png.generateStatic(browser, content, key);
}
- const page = await browser.newPage();
- const html = toHTML(svgData);
-
- await page.setContent(html);
- const svg = await getSVGElement(page);
- await stopAnimation(page);
-
- let index = 1;
- const frameLimit = 300;
- let breakRendering = false;
- let prevImg: Buffer;
-
- // Rendering frames till `imgN` matched to `imgN-1` (When Animation is done)
- while (!breakRendering) {
- if (index > frameLimit) {
- throw new Error("Reached the frame limit.");
- }
-
- resumeAnimation(page);
- const img = await screenshot(svg);
- stopAnimation(page);
-
- if (index > 1) {
- // @ts-ignore
- const diff = matchImages(prevImg, img);
- if (diff <= 100) {
- breakRendering = !breakRendering;
- }
- }
- const frame = frameNumber(index, 3);
- const key = `${path.basename(svgFilePath, ".svg")}-${frame}.png`;
-
- console.log("Saving", key, "...");
- saveFrameImage(key, img);
+ for (let { key, content } of svg.getAnimated()) {
+ console.log(" -> Saving", key, "...");
- prevImg = img;
- ++index;
+ content = SVGHandler.colorSvg(content, color);
+ await png.generateAnimated(browser, content, key);
}
- await page.close();
+ await browser.close();
}
- await browser.close();
};
main();
diff --git a/builder/Makefile b/builder/Makefile
index 4ee31b6..11b0def 100644
--- a/builder/Makefile
+++ b/builder/Makefile
@@ -1,8 +1,9 @@
-
-all: clean setup build
+bitmaps_dir = "../bitmaps"
.PHONY: all
+all: clean setup build
+
.ONESHELL:
SHELL:=/bin/bash
@@ -25,11 +26,14 @@ setup: setup.py
@. venv/bin/activate; python3 setup.py install --record files.txt
build: setup build.py
- @. venv/bin/activate; python3 build.py --xsizes $(X_SIZES) --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE)
+ @. venv/bin/activate; python3 build.py -p "$(bitmaps_dir)/GoogleDot-Blue" --xsizes $(X_SIZES) --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE)
+ @. venv/bin/activate; python3 build.py -p "$(bitmaps_dir)/GoogleDot-Black" --xsizes $(X_SIZES) --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE)
build_unix: setup build.py
- @. venv/bin/activate; python3 build.py unix --xsizes $(X_SIZES)
+ @. venv/bin/activate; python3 build.py unix -p "$(bitmaps_dir)/GoogleDot-Blue" --xsizes $(X_SIZES)
+ @. venv/bin/activate; python3 build.py unix -p "$(bitmaps_dir)/GoogleDot-Black" --xsizes $(X_SIZES)
build_windows: setup build.py
- @. venv/bin/activate; python3 build.py windows --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE)
+ @. venv/bin/activate; python3 build.py windows -p "$(bitmaps_dir)/GoogleDot-Blue" --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE)
+ @. venv/bin/activate; python3 build.py windows -p "$(bitmaps_dir)/GoogleDot-Black" --win-size $(WIN_SIZE) --win-canvas-size $(WIN_CANVAS_SIZE)
diff --git a/builder/build.py b/builder/build.py
index 376e787..e3e17ac 100644
--- a/builder/build.py
+++ b/builder/build.py
@@ -5,7 +5,7 @@
from pathlib import Path
from gbpkg.configure import get_config
-from gbpkg.generator import xbuild, wbuild, build
+from gbpkg.generator import Info, build, wbuild, xbuild
parser = argparse.ArgumentParser(
prog="google_dot_builder",
@@ -95,9 +95,17 @@
args = parser.parse_args()
bitmaps_dir = Path(args.png_dir)
+name = bitmaps_dir.stem
-x_out_dir = Path(args.out_dir) / "GoogleDot"
-win_out_dir = Path(args.out_dir) / "GoogleDot_Windows"
+comments = {
+ "GoogleDot-Blue": "Blue cursor theme inspired on Google",
+ "GoogleDot-Black": "Black cursor theme inspired on Google",
+}
+
+x_out_dir = Path(args.out_dir) / name
+win_out_dir = Path(args.out_dir) / f"{name}-Windows"
+
+print(f"Getting '{name}' bitmaps ready for build...")
config = get_config(
bitmaps_dir,
@@ -106,9 +114,11 @@
win_size=args.win_size,
)
+info = Info(name=name, comment=comments.get(name, f"{name} Cursors"))
+
if args.platform == "unix":
- xbuild(config, x_out_dir)
+ xbuild(config, x_out_dir, info)
elif args.platform == "windows":
- wbuild(config, win_out_dir)
+ wbuild(config, win_out_dir, info)
else:
- build(config, x_out_dir, win_out_dir)
+ build(config, x_out_dir, win_out_dir, info)
diff --git a/builder/gbpkg/configure.py b/builder/gbpkg/configure.py
index f818382..0e7309d 100644
--- a/builder/gbpkg/configure.py
+++ b/builder/gbpkg/configure.py
@@ -4,10 +4,8 @@
from typing import Any, Dict, Tuple, TypeVar
from clickgen.util import PNGProvider
-
from gbpkg.constants import WIN_CURSORS_CFG, WIN_DELAY, X_CURSORS_CFG, X_DELAY
-
X = TypeVar("X")
diff --git a/builder/gbpkg/constants.py b/builder/gbpkg/constants.py
index 0c727c9..1a8db05 100644
--- a/builder/gbpkg/constants.py
+++ b/builder/gbpkg/constants.py
@@ -4,8 +4,6 @@
from typing import Dict
# Info
-THEME_NAME = "GoogleDot"
-COMMENT = "Cursor theme inspired on Google"
AUTHOR = "Kaiz Khatri"
URL = "https://github.com/ful1e5/Google_Cursor"
diff --git a/builder/gbpkg/generator.py b/builder/gbpkg/generator.py
index c5fd773..c82830d 100644
--- a/builder/gbpkg/generator.py
+++ b/builder/gbpkg/generator.py
@@ -2,20 +2,21 @@
# -*- coding: utf-8 -*-
from pathlib import Path
-from typing import Any, Dict
+from typing import Any, Dict, NamedTuple
from clickgen.builders import WindowsCursor, XCursor
from clickgen.core import CursorAlias
from clickgen.packagers import WindowsPackager, XPackager
-
-from gbpkg.constants import AUTHOR, COMMENT, THEME_NAME, URL
+from gbpkg.constants import AUTHOR, URL
from gbpkg.symlinks import add_missing_xcursor
-def xbuild(
- config: Dict[str, Dict[str, Any]],
- x_out_dir: Path,
-) -> None:
+class Info(NamedTuple):
+ name: str
+ comment: str
+
+
+def xbuild(config: Dict[str, Dict[str, Any]], x_out_dir: Path, info: Info) -> None:
"""Build `GoogleDot` cursor theme for only `X11`(UNIX) platform.
```
@@ -24,6 +25,7 @@ def xbuild(
:x_out_dir: (Path) Path to the output directory,
Where the `X11` cursor theme package will generate.
It also creates a directory if not exists.
+ :info: (Dict) Content theme name & comment
```
"""
@@ -39,10 +41,10 @@ def xbuild(
XCursor.create(x_cfg, x_out_dir)
add_missing_xcursor(x_out_dir / "cursors")
- XPackager(x_out_dir, THEME_NAME, COMMENT)
+ XPackager(x_out_dir, info.name, info.comment)
-def wbuild(config: Dict[str, Dict[str, Any]], win_out_dir: Path) -> None:
+def wbuild(config: Dict[str, Dict[str, Any]], win_out_dir: Path, info: Info) -> None:
"""Build `GoogleDot` cursor theme for only `Windows` platforms.
```
@@ -51,6 +53,7 @@ def wbuild(config: Dict[str, Dict[str, Any]], win_out_dir: Path) -> None:
:win_out_dir: (Path) Path to the output directory,
Where the `Windows` cursor theme package will generate.
It also creates a directory if not exists.
+ :info: (Dict) Content theme name & comment
```
"""
@@ -76,11 +79,11 @@ def wbuild(config: Dict[str, Dict[str, Any]], win_out_dir: Path) -> None:
print(f"Building '{win_cfg.stem}' Windows Cursor...")
WindowsCursor.create(win_cfg, win_out_dir)
- WindowsPackager(win_out_dir, THEME_NAME, COMMENT, AUTHOR, URL)
+ WindowsPackager(win_out_dir, info.name, info.comment, AUTHOR, URL)
def build(
- config: Dict[str, Dict[str, Any]], x_out_dir: Path, win_out_dir: Path
+ config: Dict[str, Dict[str, Any]], x_out_dir: Path, win_out_dir: Path, info: Info
) -> None:
"""Build `GoogleDot` cursor theme for `X11` & `Windows` platforms.
@@ -94,6 +97,7 @@ def build(
:win_out_dir: (Path) Path to the output directory,
Where the `Windows` cursor theme package will generate.
It also creates a directory if not exists.
+ :info: (Dict) Content theme name & comment
```
"""
@@ -125,6 +129,6 @@ def win_build(item: Dict[str, Any], alias: CursorAlias) -> None:
win_build(item, alias)
add_missing_xcursor(x_out_dir / "cursors")
- XPackager(x_out_dir, THEME_NAME, COMMENT)
+ XPackager(x_out_dir, info.name, info.comment)
- WindowsPackager(win_out_dir, THEME_NAME, COMMENT, AUTHOR, URL)
+ WindowsPackager(win_out_dir, info.name, info.comment, AUTHOR, URL)
diff --git a/builder/gbpkg/symlinks.py b/builder/gbpkg/symlinks.py
index e00a340..b745d69 100644
--- a/builder/gbpkg/symlinks.py
+++ b/builder/gbpkg/symlinks.py
@@ -55,19 +55,9 @@ def add_missing_xcursor(directory) -> None:
"diamond_cross",
"tcross",
"color-picker",
- # crosshair
"crosshair",
],
},
- # {
- # "src": "crossed_circle",
- # "links": [
- # "03b6e0fcb3499374a867c041f52298f0",
- # "not-allowed",
- # "forbidden",
- # "circle",
- # ],
- # },
{
"src": "dnd_no_drop",
"links": [
@@ -96,7 +86,6 @@ def add_missing_xcursor(directory) -> None:
"links": [
"arrow",
"default",
- # center_ptr symlinks
"center_ptr",
],
},
@@ -109,7 +98,6 @@ def add_missing_xcursor(directory) -> None:
"progress",
],
},
- # {"src": "left_side", "links": ["w-resize", "right_side", "e-resize"]},
{
"src": "link",
"links": [
@@ -159,7 +147,6 @@ def add_missing_xcursor(directory) -> None:
"size-hor",
"size_hor",
"split_h",
- # left_side symlinks
"left_side",
"w-resize",
"right_side",
@@ -181,14 +168,12 @@ def add_missing_xcursor(directory) -> None:
"size_ver",
"split_v",
"v_double_arrow",
- # top_side symlinks
"top_side",
"s-resize",
"n-resize",
"bottom_side",
],
},
- # {"src": "top_side", "links": ["s-resize", "n-resize", "bottom_side"]},
{"src": "wait", "links": ["watch"]},
{"src": "X_cursor", "links": ["pirate", "x-cursor"]},
{"src": "xterm", "links": ["ibeam", "text"]},
diff --git a/builder/setup.py b/builder/setup.py
index 2ddf303..b0e6efb 100644
--- a/builder/setup.py
+++ b/builder/setup.py
@@ -5,7 +5,7 @@
setup(
name="gbpkg",
- version="1.0.1",
+ version="1.1.0",
description="Generate 'GoogleDot' cursor theme from PNGs file",
url="https://github.com/ful1e5/Google_Cursor",
packages=["gbpkg"],
diff --git a/svg/animated/wait.svg b/svg/animated/wait.svg
index 9e14168..674cf1e 100644
--- a/svg/animated/wait.svg
+++ b/svg/animated/wait.svg
@@ -1,122 +1,47 @@
-
diff --git a/svg/static/X_cursor.svg b/svg/static/X_cursor.svg
index 055e57e..6f0b91a 100644
--- a/svg/static/X_cursor.svg
+++ b/svg/static/X_cursor.svg
@@ -1,18 +1,7 @@
-
-
-
-
-
+
+
+
+
+
+
diff --git a/svg/static/bottom_left_corner.svg b/svg/static/bottom_left_corner.svg
index 6c36adf..f7871a3 100644
--- a/svg/static/bottom_left_corner.svg
+++ b/svg/static/bottom_left_corner.svg
@@ -1,66 +1,24 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/bottom_right_corner.svg b/svg/static/bottom_right_corner.svg
index 1b7f868..acd7df3 100644
--- a/svg/static/bottom_right_corner.svg
+++ b/svg/static/bottom_right_corner.svg
@@ -1,66 +1,24 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/bottom_tee.svg b/svg/static/bottom_tee.svg
index 9c4fb6a..06e5c90 100644
--- a/svg/static/bottom_tee.svg
+++ b/svg/static/bottom_tee.svg
@@ -1,63 +1,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/context-menu.svg b/svg/static/context-menu.svg
index 816edb3..a400e19 100644
--- a/svg/static/context-menu.svg
+++ b/svg/static/context-menu.svg
@@ -1,55 +1,25 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/copy.svg b/svg/static/copy.svg
index 09ab8f4..eb5c423 100644
--- a/svg/static/copy.svg
+++ b/svg/static/copy.svg
@@ -1,98 +1,36 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/cross.svg b/svg/static/cross.svg
index a584ef2..c878905 100644
--- a/svg/static/cross.svg
+++ b/svg/static/cross.svg
@@ -1,52 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/dnd_no_drop.svg b/svg/static/dnd_no_drop.svg
index f1ea5b3..a9e6a3a 100644
--- a/svg/static/dnd_no_drop.svg
+++ b/svg/static/dnd_no_drop.svg
@@ -1,56 +1,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/dotbox.svg b/svg/static/dotbox.svg
index 3420bb1..c4d5f42 100644
--- a/svg/static/dotbox.svg
+++ b/svg/static/dotbox.svg
@@ -1,56 +1,26 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/hand1.svg b/svg/static/hand1.svg
index 7386e1d..c76d2ee 100644
--- a/svg/static/hand1.svg
+++ b/svg/static/hand1.svg
@@ -1,52 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/hand2.svg b/svg/static/hand2.svg
index bbada2f..52350ce 100644
--- a/svg/static/hand2.svg
+++ b/svg/static/hand2.svg
@@ -1,52 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/left_ptr.svg b/svg/static/left_ptr.svg
index a00f8ed..6a7b4d7 100644
--- a/svg/static/left_ptr.svg
+++ b/svg/static/left_ptr.svg
@@ -1,52 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/left_tee.svg b/svg/static/left_tee.svg
index 8b397e7..3def7fe 100644
--- a/svg/static/left_tee.svg
+++ b/svg/static/left_tee.svg
@@ -1,63 +1,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/link.svg b/svg/static/link.svg
index 872bbd7..3708037 100644
--- a/svg/static/link.svg
+++ b/svg/static/link.svg
@@ -1,60 +1,24 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/ll_angle.svg b/svg/static/ll_angle.svg
index 723060c..9f0830a 100644
--- a/svg/static/ll_angle.svg
+++ b/svg/static/ll_angle.svg
@@ -1,58 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/lr_angle.svg b/svg/static/lr_angle.svg
index b7c2803..6e03ff6 100644
--- a/svg/static/lr_angle.svg
+++ b/svg/static/lr_angle.svg
@@ -1,58 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/move.svg b/svg/static/move.svg
index 6dc9c12..c292faf 100644
--- a/svg/static/move.svg
+++ b/svg/static/move.svg
@@ -1,64 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/plus.svg b/svg/static/plus.svg
index 5b7e06d..2e0a00e 100644
--- a/svg/static/plus.svg
+++ b/svg/static/plus.svg
@@ -1,56 +1,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/question_arrow.svg b/svg/static/question_arrow.svg
index 5a3d143..8dfced7 100644
--- a/svg/static/question_arrow.svg
+++ b/svg/static/question_arrow.svg
@@ -1,56 +1,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/right_tee.svg b/svg/static/right_tee.svg
index cc12c73..af5f7d6 100644
--- a/svg/static/right_tee.svg
+++ b/svg/static/right_tee.svg
@@ -1,68 +1,28 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/sb_down_arrow.svg b/svg/static/sb_down_arrow.svg
index b060888..a9910dc 100644
--- a/svg/static/sb_down_arrow.svg
+++ b/svg/static/sb_down_arrow.svg
@@ -1,63 +1,27 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/sb_h_double_arrow.svg b/svg/static/sb_h_double_arrow.svg
index 0f7fcda..784160a 100644
--- a/svg/static/sb_h_double_arrow.svg
+++ b/svg/static/sb_h_double_arrow.svg
@@ -1,66 +1,24 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/sb_left_arrow.svg b/svg/static/sb_left_arrow.svg
index e295161..37ab699 100644
--- a/svg/static/sb_left_arrow.svg
+++ b/svg/static/sb_left_arrow.svg
@@ -1,63 +1,27 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/sb_right_arrow.svg b/svg/static/sb_right_arrow.svg
index e6577ba..fc01d2d 100644
--- a/svg/static/sb_right_arrow.svg
+++ b/svg/static/sb_right_arrow.svg
@@ -1,63 +1,27 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/sb_up_arrow.svg b/svg/static/sb_up_arrow.svg
index 9102a76..65a2dd3 100644
--- a/svg/static/sb_up_arrow.svg
+++ b/svg/static/sb_up_arrow.svg
@@ -1,63 +1,27 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/sb_v_double_arrow.svg b/svg/static/sb_v_double_arrow.svg
index 348711c..2dcb28c 100644
--- a/svg/static/sb_v_double_arrow.svg
+++ b/svg/static/sb_v_double_arrow.svg
@@ -1,66 +1,24 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/top_tee.svg b/svg/static/top_tee.svg
index 6e18b7d..6562ba3 100644
--- a/svg/static/top_tee.svg
+++ b/svg/static/top_tee.svg
@@ -1,63 +1,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/ul_angle.svg b/svg/static/ul_angle.svg
index 2933bae..83afe97 100644
--- a/svg/static/ul_angle.svg
+++ b/svg/static/ul_angle.svg
@@ -1,58 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/ur_angle.svg b/svg/static/ur_angle.svg
index f7811c8..aa990e4 100644
--- a/svg/static/ur_angle.svg
+++ b/svg/static/ur_angle.svg
@@ -1,58 +1,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/vertical-text.svg b/svg/static/vertical-text.svg
index 3d5f002..2c4de17 100644
--- a/svg/static/vertical-text.svg
+++ b/svg/static/vertical-text.svg
@@ -1,19 +1,8 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/svg/static/wayland-cursor.svg b/svg/static/wayland-cursor.svg
index 4bdfce9..40a0fc6 100644
--- a/svg/static/wayland-cursor.svg
+++ b/svg/static/wayland-cursor.svg
@@ -1,19 +1,8 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/svg/static/xterm.svg b/svg/static/xterm.svg
index d2d933d..de97f4d 100644
--- a/svg/static/xterm.svg
+++ b/svg/static/xterm.svg
@@ -1,19 +1,8 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/svg/static/zoom-in.svg b/svg/static/zoom-in.svg
index 6181608..6f3667b 100644
--- a/svg/static/zoom-in.svg
+++ b/svg/static/zoom-in.svg
@@ -1,56 +1,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/svg/static/zoom-out.svg b/svg/static/zoom-out.svg
index e9c39b3..7d20317 100644
--- a/svg/static/zoom-out.svg
+++ b/svg/static/zoom-out.svg
@@ -1,56 +1,23 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+