mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-04 13:29:13 -04:00
Merge 045075a051
into e1b4f9aafe
This commit is contained in:
commit
1f2f5d7b0f
7 changed files with 1849 additions and 64 deletions
2
components.d.ts
vendored
2
components.d.ts
vendored
|
@ -127,6 +127,7 @@ declare module '@vue/runtime-core' {
|
|||
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
|
||||
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
|
||||
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
|
||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||
NCode: typeof import('naive-ui')['NCode']
|
||||
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
|
@ -152,6 +153,7 @@ declare module '@vue/runtime-core' {
|
|||
PdfSignatureDetails: typeof import('./src/tools/pdf-signature-checker/components/pdf-signature-details.vue')['default']
|
||||
PercentageCalculator: typeof import('./src/tools/percentage-calculator/percentage-calculator.vue')['default']
|
||||
PhoneParserAndFormatter: typeof import('./src/tools/phone-parser-and-formatter/phone-parser-and-formatter.vue')['default']
|
||||
Potrace: typeof import('./src/tools/potrace/potrace.vue')['default']
|
||||
QrCodeGenerator: typeof import('./src/tools/qr-code-generator/qr-code-generator.vue')['default']
|
||||
RandomPortGenerator: typeof import('./src/tools/random-port-generator/random-port-generator.vue')['default']
|
||||
ResultRow: typeof import('./src/tools/ipv4-range-expander/result-row.vue')['default']
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
"@tiptap/starter-kit": "2.1.6",
|
||||
"@tiptap/vue-3": "2.0.3",
|
||||
"@types/figlet": "^1.5.8",
|
||||
"@types/potrace": "^2.1.5",
|
||||
"@vicons/material": "^0.12.0",
|
||||
"@vicons/tabler": "^0.12.0",
|
||||
"@vueuse/core": "^10.3.0",
|
||||
|
@ -80,6 +81,7 @@
|
|||
"pdf-signature-reader": "^1.4.2",
|
||||
"pinia": "^2.0.34",
|
||||
"plausible-tracker": "^0.3.8",
|
||||
"potrace": "^2.1.8",
|
||||
"qrcode": "^1.5.1",
|
||||
"sql-formatter": "^13.0.0",
|
||||
"ua-parser-js": "^1.0.35",
|
||||
|
@ -132,6 +134,7 @@
|
|||
"unplugin-icons": "^0.17.0",
|
||||
"unplugin-vue-components": "^0.25.0",
|
||||
"vite": "^4.4.9",
|
||||
"vite-plugin-node-polyfills": "^0.22.0",
|
||||
"vite-plugin-pwa": "^0.16.0",
|
||||
"vite-plugin-vue-markdown": "^0.23.5",
|
||||
"vite-svg-loader": "^4.0.0",
|
||||
|
|
1781
pnpm-lock.yaml
generated
1781
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -6,6 +6,7 @@ import { tool as asciiTextDrawer } from './ascii-text-drawer';
|
|||
|
||||
import { tool as textToUnicode } from './text-to-unicode';
|
||||
import { tool as safelinkDecoder } from './safelink-decoder';
|
||||
import { tool as potrace } from './potrace';
|
||||
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
|
||||
import { tool as numeronymGenerator } from './numeronym-generator';
|
||||
import { tool as macAddressGenerator } from './mac-address-generator';
|
||||
|
@ -132,7 +133,13 @@ export const toolsByCategory: ToolCategory[] = [
|
|||
},
|
||||
{
|
||||
name: 'Images and videos',
|
||||
components: [qrCodeGenerator, wifiQrCodeGenerator, svgPlaceholderGenerator, cameraRecorder],
|
||||
components: [
|
||||
qrCodeGenerator,
|
||||
wifiQrCodeGenerator,
|
||||
svgPlaceholderGenerator,
|
||||
cameraRecorder,
|
||||
potrace,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Development',
|
||||
|
|
12
src/tools/potrace/index.ts
Normal file
12
src/tools/potrace/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { ArrowsShuffle } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Image to SVG (potrace)',
|
||||
path: '/potrace',
|
||||
description: 'Convert an raster image to vectorial SVG',
|
||||
keywords: ['potrace', 'image', 'svg', 'raster', 'vectorial'],
|
||||
component: () => import('./potrace.vue'),
|
||||
icon: ArrowsShuffle,
|
||||
createdAt: new Date('2024-05-11'),
|
||||
});
|
104
src/tools/potrace/potrace.vue
Normal file
104
src/tools/potrace/potrace.vue
Normal file
|
@ -0,0 +1,104 @@
|
|||
<script setup lang="ts">
|
||||
import { Buffer } from 'node:buffer';
|
||||
import type { Ref } from 'vue';
|
||||
import potrace from 'potrace';
|
||||
import { Base64 } from 'js-base64';
|
||||
import TextareaCopyable from '@/components/TextareaCopyable.vue';
|
||||
|
||||
async function traceAsync(input: Buffer) {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
potrace.trace(input, (err: Error | null, svg: string) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(svg);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function posterizeAsync(input: Buffer) {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
potrace.posterize(input,
|
||||
(err: Error | null, svg: string) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(svg);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function file2Buffer(file: File) {
|
||||
return new Promise<Buffer>((resolve, _reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => {
|
||||
const buffer = Buffer.from(reader.result as ArrayBuffer);
|
||||
resolve(buffer);
|
||||
});
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
}
|
||||
|
||||
const posterize = ref(true);
|
||||
const fileInput = ref() as Ref<File>;
|
||||
const svg = computedAsync(async () => {
|
||||
const trace = !posterize.value;
|
||||
const file = fileInput.value;
|
||||
if (!file) {
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
const buffer = await file2Buffer(file);
|
||||
return (trace ? await traceAsync(buffer) : await posterizeAsync(buffer));
|
||||
}
|
||||
catch (e: any) {
|
||||
return e.toString();
|
||||
}
|
||||
});
|
||||
|
||||
const svgBase64 = computed(() => svg.value ? `data:image/svg+xml;base64,${Base64.encode(svg.value)}` : '');
|
||||
|
||||
async function onUpload(file: File) {
|
||||
if (file) {
|
||||
fileInput.value = file;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="max-width: 600px;">
|
||||
<c-file-upload
|
||||
title="Drag and drop an image here, or click to select a file"
|
||||
:paste-image="true"
|
||||
@file-upload="onUpload"
|
||||
/>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<n-checkbox v-model:checked="posterize" mt-2>
|
||||
Posterize?
|
||||
</n-checkbox>
|
||||
</div>
|
||||
|
||||
<n-divider />
|
||||
|
||||
<div>
|
||||
<h3>Potrace result</h3>
|
||||
<TextareaCopyable
|
||||
:value="svg"
|
||||
word-wrap
|
||||
/>
|
||||
|
||||
<n-divider />
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img width="150" :src="svgBase64" style="background-color: white">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
::v-deep(.n-upload-trigger) {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -15,6 +15,7 @@ import { VitePWA } from 'vite-plugin-pwa';
|
|||
import markdown from 'vite-plugin-vue-markdown';
|
||||
import svgLoader from 'vite-svg-loader';
|
||||
import { configDefaults } from 'vitest/config';
|
||||
import { nodePolyfills } from 'vite-plugin-node-polyfills'
|
||||
|
||||
const baseUrl = process.env.BASE_URL ?? '/';
|
||||
|
||||
|
@ -97,6 +98,7 @@ export default defineConfig({
|
|||
resolvers: [NaiveUiResolver(), IconsResolver({ prefix: 'icon' })],
|
||||
}),
|
||||
Unocss(),
|
||||
nodePolyfills(),
|
||||
],
|
||||
base: baseUrl,
|
||||
resolve: {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue