Fotoupload im Personenformular ergänzen und Initialen-Platzhalter aussortieren
Fotofeld mit Dateiauswahl, Einfügen per Strg+V, Zwischenablage-Schaltfläche und Drag & Drop; sichtbarer Foto-Button je Zeile. Foto wird beim Anlegen oder Speichern übernommen bzw. entfernt. Nextcloud meldet generierte Initialen-Avatare teils als eigene Profilbilder. Der Import erkennt Bilder mit dominanter Hintergrundfarbe und lässt sie aus; „Platzhalter-Fotos entfernen" bereinigt bereits importierte. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
8a0f903a40
commit
9ed59bcf56
6 files changed
+295
-7
No files matched your search
@@ -0,0 +1,43 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import zlib from 'node:zlib';
|
||||
import { pngDominantShare, looksLikePlaceholder } from '../server/image.js';
|
||||
|
||||
/** Minimaler PNG-Encoder für Testbilder (RGB, 8 Bit, Filter 0). */
|
||||
function png(width, height, pixel) {
|
||||
const raw = Buffer.alloc((width * 3 + 1) * height);
|
||||
for (let y = 0; y < height; y += 1) {
|
||||
raw[y * (width * 3 + 1)] = 0;
|
||||
for (let x = 0; x < width; x += 1) {
|
||||
const [r, g, b] = pixel(x, y);
|
||||
raw.set([r, g, b], y * (width * 3 + 1) + 1 + x * 3);
|
||||
}
|
||||
}
|
||||
const chunk = (type, body) => {
|
||||
const len = Buffer.alloc(4); len.writeUInt32BE(body.length);
|
||||
const crc = Buffer.alloc(4); crc.writeUInt32BE(zlib.crc32(Buffer.concat([Buffer.from(type), body])) >>> 0);
|
||||
return Buffer.concat([len, Buffer.from(type), body, crc]);
|
||||
};
|
||||
const ihdr = Buffer.alloc(13); ihdr.writeUInt32BE(width, 0); ihdr.writeUInt32BE(height, 4); ihdr[8] = 8; ihdr[9] = 2;
|
||||
return Buffer.concat([Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), chunk('IHDR', ihdr), chunk('IDAT', zlib.deflateSync(raw)), chunk('IEND', Buffer.alloc(0))]);
|
||||
}
|
||||
|
||||
test('Initialen-Platzhalter: eine Hintergrundfarbe mit Buchstaben wird erkannt', () => {
|
||||
const img = png(64, 64, (x, y) => (x > 20 && x < 44 && y > 24 && y < 40 ? [40, 40, 40] : [230, 200, 150]));
|
||||
const info = pngDominantShare(img);
|
||||
assert.ok(info.share > 0.85, `share ${info.share}`);
|
||||
assert.ok(looksLikePlaceholder(img));
|
||||
});
|
||||
|
||||
test('Foto mit Farbverlauf ist kein Platzhalter', () => {
|
||||
const img = png(64, 64, (x, y) => [x * 4, y * 4, (x + y) * 2]);
|
||||
const info = pngDominantShare(img);
|
||||
assert.ok(info.share < 0.1, `share ${info.share}`);
|
||||
assert.ok(!looksLikePlaceholder(img));
|
||||
});
|
||||
|
||||
test('JPEG und Unbekanntes werden nie als Platzhalter gewertet', () => {
|
||||
assert.equal(looksLikePlaceholder(Buffer.from([0xff, 0xd8, 0xff, 0xe0, 0, 0, 0, 0, 0, 0, 0, 0, 0])), false);
|
||||
assert.equal(pngDominantShare(Buffer.from('kein png')), null);
|
||||
assert.equal(looksLikePlaceholder(null), false);
|
||||
});
|
||||
Reference in new issue
Block a user