Compare commits

..
20 Commits
Author SHA1 Message Date
Michael StanglandClaude Opus 5.5 c5598f2304 Doku: Optionen mit Kurz- und Langform
- Tabellen für piccreate, svgconvert, webscan mit -x, --lang <wert>
- neue Optionstabelle für glyphextract, --version in der Übersicht

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:40:26 +02:00
Michael StanglandClaude Opus 5.5 6bdaa62a7d Optionsschalter vereinheitlicht, .sh-Skripte von überall aufrufbar
- gleiche Aufgabe = gleicher Schalter in allen Programmen:
  -c/--config (Alias --cfg), -i, -o/--outdir, --section, --format,
  -f/--force, -j, -t/--test, -y/--dry, -v
- svgconvert: -o statt -s (Alias --svgfolder), --section statt --format
  für die Sektion (Standard png), --format überschreibt das Bildformat
- webscan: --timeout und --browser nur noch lang (kollidierten mit -t/-b)
- glyphextract: -i nimmt Glyph oder Font, -e extrahiert (statt -f/-t)
- setenv.sh ermittelt das Projektverzeichnis selbst, alle .sh laden es
  relativ zum Skript; extract-font/fonttest reichen weitere Optionen durch
- Hilfetexte korrigiert, doc/README.md: Abschnitt "Gemeinsame Optionen"

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:37:20 +02:00
Michael StanglandClaude Opus 5.5 28f2bcdb32 svgconvert.sh ruft svgconvert.py auf, generationlib entfernt
- bin/svgconvert.sh ist jetzt ein Wrapper wie svgconvert.bat
  (gleiche Optionen unter Linux und Windows)
- svgconvert.py: Endung .cfg/.csv bei -c/-i optional
- bin/generationlib (nur von svgconvert.sh benutzt) entfernt
- doc/README.md angepasst

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:31:36 +02:00
Michael StanglandClaude Opus 5.5 29c066b45a Windows-Unterstützung: .bat-Skripte, Font-Werkzeuge in Python
- libs/glyphextract.py ersetzt die grep/sed/bc-Logik von extract-glyph.sh,
  extract-font.sh und fonttest.sh (die .sh rufen es nur noch auf);
  schreibt FONTNAME= und <font>.cfg nach $PICCREATE_OUT
- bin/*.bat: setenv, piccreate, svgconvert, webscan, extract-glyph,
  extract-font, fonttest, setup-venv; .gitattributes für CRLF
- piccreate.py: Hintergrund als file:// URL (Windows-Pfade)
- svgconvert.py: magick statt convert, Configs/CSV als UTF-8
- webscan.py: Installationshinweise für Windows
- doc/README.md: Abschnitt Windows, Font-Werkzeuge aktualisiert

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:29:02 +02:00
Michael StanglandClaude Opus 5.5 4897883938 bin/setup-venv.sh: Python-Umgebung .venv einrichten
- legt .venv an (--upgrade-deps) und installiert requirements.txt,
  überspringt das, wenn .venv schon existiert
- Browser für webscan: Arch -> Hinweis auf System-Chromium,
  sonst Playwright-Chromium installieren
- doc/README.md verweist auf das Skript

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:18:39 +02:00
Michael StanglandClaude Opus 5.5 c884f4fd92 webscan: Prüfung auf Playwright und Browser mit Installationshinweisen
- fehlt Playwright oder der Browser, werden die passenden Befehle für
  Ubuntu/Debian bzw. Arch ausgegeben (erkannt über /etc/os-release),
  Abbruch mit Exit-Code 2
- doc/README.md: Einrichtung getrennt nach Ubuntu und Arch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:16:04 +02:00
Michael StanglandClaude Opus 5.5 7bd43e9e00 webscan mit Playwright statt CutyCapt
- libs/webscan.py: Screenshots von Webseiten (Kundennummer;URL) mit
  Playwright/Chromium, parallel, Größe aus [webscan] der Konfiguration;
  der Zuschnitt mit convert entfällt
- bin/webscan.sh ruft webscan.py auf (Python aus .venv, falls vorhanden)
- bin/webscan und libs/CutyCapt (Qt4-WebKit, 2012) entfernt
- requirements.txt, .gitignore, Abschnitt in doc/README.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:13:39 +02:00
Michael StanglandClaude Opus 5.5 31c8d97817 Dokumentation in doc/ mit Beispielbildern
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:00:40 +02:00
Michael StanglandClaude Opus 5.5 75db9b7024 Drei alte Fehler behoben
- create_picture: </svg> und Dateiausgabe erst nach allen Zeilen
  (vorher pro Zeile, kaputtes SVG bei mehreren Zeilen)
- yalign=bottom: word_height wird jetzt berechnet (vorher NameError)
- CompleteEnding: ValueError statt undefiniertem Csv2CouchError

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 08:55:46 +02:00
Michael StanglandClaude Opus 5.5 d4cb697f14 Auf Python 3 portiert, Django-Abhängigkeit entfernt
- piccreate.py, utils.py: print, configparser, has_key -> in, UTF-8 I/O
- smart_str/smart_unicode entfernt, eigenes slugify() in utils.py
- configparser mit strict=False (doppelte [+] Sektion in den Font-cfgs)
- Shebangs und bin/piccreate auf python3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 08:53:55 +02:00
Michael Stangl dc8e5230e2 Aus Backup übernommen: progressbar.py, svgconvert.py, font.cfg (density=200) 2026-04-04 20:30:34 +02:00
Michael Stangl 4900c8027f - merge 2012-09-21 11:05:50 +02:00
Michael Stangl 621e2f2fe0 Merge branch 'master' of git+ssh://gitbox/piccreate 2012-09-21 11:04:21 +02:00
Michael Stangl a2d4d6794b - Hintergrundbild mit abgerundeten Ecken dazu 2012-09-21 11:03:55 +02:00
Kamil Bugajski 6f77f4dbc4 Merge branch 'master' of git+ssh://gitbox/piccreate
Conflicts:
	bin/webscan
	cfg/webscan.cfg
	libs/CutyCapt/CutyCapt.cpp
	libs/CutyCapt/CutyCapt.hpp
	libs/CutyCapt/CutyCapt.o
	libs/CutyCapt/CutyCapt.pro
	libs/CutyCapt/Makefile
	libs/CutyCapt/google.jpg
	libs/CutyCapt/moc_CutyCapt.cpp
	libs/CutyCapt/moc_CutyCapt.o
	libs/CutyCapt/readme.txt
2012-09-19 10:34:00 +02:00
Kamil Bugajski 6109588792 webscan eingefügt 2012-09-19 10:15:42 +02:00
Kamil Bugajski 679cd04724 umbenennen der fertig geschnittenen dateien 2012-08-31 16:54:36 +02:00
Kamil Bugajski ceaca4400c CutyCapt für Screenshots in die libs 2012-08-31 16:27:05 +02:00
Kamil Bugajski 80b0ab786a screenshots von internetseiten erzeugen 2012-08-31 16:24:49 +02:00
Kamil Bugajski 507450d374 switch für verschiedene bildformat-ausgaben 2012-08-28 11:00:50 +02:00
34 changed files with 1644 additions and 403 deletions
+3
View File
@@ -0,0 +1,3 @@
*.bat text eol=crlf
*.sh text eol=lf
*.py text eol=lf
+3
View File
@@ -0,0 +1,3 @@
.venv/
__pycache__/
*.pyc
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

+10
View File
@@ -0,0 +1,10 @@
@echo off
rem Glyphen und Font-Config eines Fonts nach %PICCREATE_OUT% schreiben
setlocal
call "%~dp0setenv.bat" || exit /b 1
if "%~1"=="" (
echo please give a directory in the in folder with .svg glyphs as input
exit /b 1
)
"%PICCREATE_PYTHON%" "%PICCREATE_LIBS%\glyphextract.py" -i %1 -e %2 %3 %4 %5 %6 %7 %8 %9
exit /b %errorlevel%
+4 -21
View File
@@ -1,26 +1,9 @@
#!/bin/bash
. setenv.sh
. "$(dirname "${BASH_SOURCE[0]}")/setenv.sh"
if [ -z "$1" ]; then
echo "please give a directory in the templates folder with .svg glyphs as input"
echo "please give a directory in the in folder with .svg glyphs as input"
exit 1
fi
if [ -d "$PICCREATE_IN/$1" ]; then
export SOURCE="$PICCREATE_IN/$1"
elif [ ! -d "$1" ]; then
export SOURCE="$1"
else
echo "directory does not exist"
echo "please give a directory with .svg files as input"
exit 1
fi
export TARGET=$PICCREATE_OUT
for i in `ls $SOURCE/*.svg`
do
echo `basename $i .svg`
$PICCREATE_BIN/extract-glyph.sh -i $i -e
done
CONFIG=`basename $SOURCE`
( cat $TARGET/*.cfg >> $CONFIG.cfg_ ; rm $TARGET/*.cfg; mv $CONFIG.cfg_ $CONFIG.cfg)
# Glyphen und <font>.cfg eines Fonts nach $PICCREATE_OUT schreiben
python3 $PICCREATE_LIBS/glyphextract.py -i "$1" -e "${@:2}"
+6
View File
@@ -0,0 +1,6 @@
@echo off
rem Einen Buchstaben extrahieren (-i datei.svg [-e])
setlocal
call "%~dp0setenv.bat" || exit /b 1
"%PICCREATE_PYTHON%" "%PICCREATE_LIBS%\glyphextract.py" %*
exit /b %errorlevel%
+5 -147
View File
@@ -1,149 +1,7 @@
#!/bin/bash
. setenv.sh
if [ -z "$PICCREATE" ]; then
echo "please source the environment"
exit 1
fi
O_ARGS=0
E_OPTERROR=85
USAGE="create a jpg file with the purl inside
Usage: `basename $0` [-hie]
-i id
-e extract
Examples:
to test the glyph later with inkscape:
`basename $0` -i ../in/buchstabe_a.svg
to extract the points and text from the glyph:
`basename $0` -i ../in/buchstabe_a.svg -e "
while getopts "i:eu:" Option
do
case $Option in
i) ID=$OPTARG;;
e) EXTRACT=1;;
*) echo "$USAGE" >&2; exit 1; # Default.
esac
done
shift $(($OPTIND - 1))
if [ -z $ID ] ; then
echo "-> no id/filename given"
echo "$USAGE" >&2
exit 1
fi
if [ -z "$ID" ]; then
echo "please give a .svg file of a glyph as input"
echo "$USAGE" >&2
exit 1
fi
if [ -s "$ID" ]; then
export BASE=`basename $ID .svg`
export GLYPH=$PICCREATE_OUT/$BASE.svg
export CONFIG=$PICCREATE_OUT/$BASE.cfg
else
echo "input file does not exist"
echo "$USAGE" >&2
exit 1
fi
# extrahiere die wichtigen Zeilen aus dem Font
cat $ID | egrep '<\<line\>|x1|x2|y1|y2|/>|style' | egrep -v '\<text\>|id=|http:' > line-$BASE.svg
cat $ID | egrep 'circle|translate|\<r\>' > circles-$BASE.svg
cat $ID | egrep '<\<text|text\>>|\<x\>=|\<y\>=|tspan|style|preserve' | egrep -v '\<transform-center-x\>|\<window-|\<id\>=' > text-$BASE.svg
# reduziere einige Zeilen um bestimmte Muster
cat line-$BASE.svg | sed -e 's/inkscape:transform-center-x=".*"//g' > $$; mv $$ line-$BASE.svg
cat line-$BASE.svg | sed -e 's/inkscape:transform-center-y=".*"//g' > $$; mv $$ line-$BASE.svg
cat text-$BASE.svg | sed -e 's/inkscape:transform-center-x=".*"//g' > $$; mv $$ text-$BASE.svg
cat text-$BASE.svg | sed -e 's/inkscape:transform-center-y=".*"//g' > $$; mv $$ text-$BASE.svg
cat circles-$BASE.svg | sed -e 's/inkscape:transform-center-x=".*"//g' > $$; mv $$ circles-$BASE.svg
cat circles-$BASE.svg | sed -e 's/inkscape:transform-center-y=".*"//g' > $$; mv $$ circles-$BASE.svg
# den eventuell alten Buchstaben vorher wegschmeissen
rm -f $GLYPH
if [ -z "$EXTRACT" ];
then
# schreib einen kompletten neuen Buchstaben zum testen
echo '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">' >> $GLYPH
cat text-$BASE.svg >> $GLYPH
cat circles-$BASE.svg >> $GLYPH
cat line-$BASE.svg >> $GLYPH
echo '</svg>' >> $GLYPH
else
# schreib nur das extract zur Verwertung als Font in die Ausgabe
#echo '<g transform="translate(${posx},${posy})">' >> $GLYPH
echo '<g transform="translate(${posx},${posy})">' >> $GLYPH
cat text-$BASE.svg >> $GLYPH
cat circles-$BASE.svg >> $GLYPH
cat line-$BASE.svg >> $GLYPH
echo '</g>' >> $GLYPH
fi
rm line-$BASE.svg
rm circles-$BASE.svg
rm text-$BASE.svg
## alle Zusatzinfos zum Buchstaben rausholen
XCOORDS=./xcoords
# hole das Minium und Maxium der X-Koordinaten um die Schrittweite des Buchstabens zu bestimmen
# zuerst aus den Texten
cat $GLYPH | egrep "\<x\>=" | sed "s/^\s\+//g;s/x=//g;s/\"//g" > $XCOORDS
# dann aus Kreisen
cat $GLYPH | egrep -v '<g' | egrep "transform=" | sed 's/^\s\+//g;s/translate//g;s/transform=//g;s/["|(|)]//g;s/,.*//g' >> $XCOORDS
# dann aus den Linien
cat $GLYPH | grep x1= | sed "s/^\s\+//g;s/x1=//g;s/\"//g" >> $XCOORDS
cat $GLYPH | grep x2= | sed "s/^\s\+//g;s/x2=//g;s/\"//g" >> $XCOORDS
# sortiere die Koordinaten numerisch
cat $XCOORDS | sort -g | uniq > $$ ; mv $$ $XCOORDS;
XMAX=`tail -n 1 $XCOORDS`
XMIN=`head -n 1 $XCOORDS`
rm $XCOORDS
YCOORDS=./ycoords
# hole das Minium und Maxium der Y-Koordinaten um die Schrittweite des Buchstabens zu bestimmen
# zuerst aus den Texten
cat $GLYPH | grep y= | sed "s/^\s\+//g;s/y=//g;s/\"//g" > $YCOORDS
# dann aus Kreisen
cat $GLYPH | egrep -v '<g' | egrep "transform=" | sed 's/^\s\+//g;s/translate//g;s/transform=//g;s/["|(|)]//g;s/.*,//g' >> $YCOORDS
# dann aus den Linien
cat $GLYPH | grep y1= | sed "s/^\s\+//g;s/y1=//g;s/\"//g" >> $YCOORDS
cat $GLYPH | grep y2= | sed "s/^\s\+//g;s/y2=//g;s/\"//g" >> $YCOORDS
# sortiere die Koordinaten numerisch
cat $YCOORDS | sort -g | uniq > $$ ; mv $$ $YCOORDS;
YMAX=`tail -n 1 $YCOORDS`
YMIN=`head -n 1 $YCOORDS`
rm $YCOORDS
XSTEP=`echo "$XMAX-$XMIN" | bc`
YSTEP=`echo "$YMAX-$YMIN" | bc`
# hole raus wieviele x1-n Muster zu ersetzen sind
NPATTERN=`cat $GLYPH | egrep ">x[0-9]+<" | sed "s/.*\(x[0-9]\+\).*/\1/g" | wc -l`
# ersetze die Muster von x1-xn durch Templatezeichen
cat $GLYPH | sed "s/>\(x[0-9]\+\)</>\${\1}</g" > $$ ; mv $$ $GLYPH;
## und in die Buchstabenconfig schreiben
rm -f $CONFIG
echo [$BASE] >> $CONFIG
echo "XSTEP=$XSTEP" >> $CONFIG
echo "XMIN=$XMIN" >> $CONFIG
echo "XMAX=$XMAX" >> $CONFIG
echo "YSTEP=$YSTEP" >> $CONFIG
echo "YMIN=$YMIN" >> $CONFIG
echo "YMAX=$YMAX" >> $CONFIG
echo "NPATTERN=$NPATTERN" >> $CONFIG
echo "FILENAME=$BASE.svg" >> $CONFIG
echo "#" >> $CONFIG
. "$(dirname "${BASH_SOURCE[0]}")/setenv.sh"
# Einen Buchstaben extrahieren
# extract-glyph.sh -i ../in/ausgangsschrift/a.svg Testbild fuer Inkscape
# extract-glyph.sh -i ../in/ausgangsschrift/a.svg -e Glyph fuer den Font
python3 $PICCREATE_LIBS/glyphextract.py "$@"
+10
View File
@@ -0,0 +1,10 @@
@echo off
rem Testbilder aller Glyphen eines Fonts nach %PICCREATE_OUT% schreiben
setlocal
call "%~dp0setenv.bat" || exit /b 1
if "%~1"=="" (
echo please give a directory in the in folder with .svg glyphs as input
exit /b 1
)
"%PICCREATE_PYTHON%" "%PICCREATE_LIBS%\glyphextract.py" -i %1 %2 %3 %4 %5 %6 %7 %8 %9
exit /b %errorlevel%
+4 -19
View File
@@ -1,24 +1,9 @@
#!/bin/bash
. setenv.sh
. "$(dirname "${BASH_SOURCE[0]}")/setenv.sh"
if [ -z "$1" ]; then
echo "please give a directory in the templates folder with .svg glyphs as input"
echo "please give a directory in the in folder with .svg glyphs as input"
exit 1
fi
if [ -d "$PICCREATE_IN/$1" ]; then
export SOURCE="$PICCREATE_IN/$1"
elif [ ! -d "$1" ]; then
export SOURCE="$1"
else
echo "directory does not exist"
echo "please give a directory with .svg files as input"
exit 1
fi
export TARGET=$PICCREATE_OUT
for i in `ls $SOURCE/*.svg`
do
echo `basename $i .svg`
$PICCREATE_BIN/extract-glyph.sh -i $i
done
# Testbilder aller Glyphen eines Fonts nach $PICCREATE_OUT schreiben
python3 $PICCREATE_LIBS/glyphextract.py -i "$1" "${@:2}"
-149
View File
@@ -1,149 +0,0 @@
#!/bin/bash
NUM=0
QUEUE=""
MAX_NPROC=2 # default
REPLACE_CMD=0 # no replacement by default
USAGE="A simple wrapper for generating jpg pictures out of svg-files in parallel.
Usage: `basename $0` [-h] [-i] [-j nb_jobs] command arg_list
-h Shows this help
-c give the path to the cfg file
-i give the path of the svg files without the last /
-k chek if the jpg already exist
-s set the sleeptime while there are too many simultanious jobs
-j Set number of simultanious jobs. Default is 2
-o directory where the files should be moved to after creation.
Default ist PICCREATE_OUT
Examples:
`basename $0` -i file -c config
`basename $0` -j 3 -i file -c config"
function cfg_parser {
INI_FILE=$1
SECTION=$2
eval `sed -n -e "/^\[$SECTION\]/,/^\s*\[/{/^[^;].*\=.*/p;}" < $INI_FILE`>> trr.txt
}
function get_filepath {
# probiere alle möglichen Kombinationen wo das file stehen könnte je
# nachdem ob es mit oder ohne Endung, mit oder ohne den Komplettpfad
# gegeben wurde
FILENAME=$1
ENDING=$2
DEFAULT_DIR=$3
if [ -s $FILENAME ] ; then
echo $FILENAME
elif [ -s $FILENAME.$ENDING ] ; then
echo "$FILENAME.$ENDING"
elif [ -s $DEFAULT_DIR/$FILENAME ] ; then
echo "$DEFAULT_DIR/$FILENAME"
elif [ -s $DEFAULT_DIR/$FILENAME.$ENDING ] ; then
echo "$DEFAULT_DIR/$FILENAME.$ENDING"
fi
return
}
# Funktion die auf globalen Parametern beruht.
# überprüft ob alle notwendigen Angaben vorhanden sind
function check_params {
if [ -z "$INPUT_FILENAME" ]; then
echo "please give input file with -i."
exit 1
fi
if [ -z "$CFG_FILENAME" ]; then
echo "please give config file with -c."
exit 1
fi
INPUT_FILE=`get_filepath $INPUT_FILENAME 'csv' $PICCREATE_WORK`
if [ ! -s $INPUT_FILE ] ; then
echo "-> input filename does not exist"
print "given was: -$INPUT_FILENAME-"
exit 1
fi
CFG_PATH=`get_filepath $CFG_FILENAME 'cfg' $PICCREATE_CFG`
if [ ! -s $CFG_PATH ]; then
echo "-> config file with this name does not exist."
print "given was: -$CFG_FILENAME-"
exit 1
fi
cfg_parser $CFG_PATH "$SECTION_NAME"
echo 'using width '$width
echo 'using height '$height
if [ -z "$width" ]; then
echo "please give width=xxx in section [$SECTION_NAME] in config $CFG_FILENAME."
exit 1
fi
if [ -z "$height" ]; then
echo "please give height=xxx in section [$SECTION_NAME] in config $CFG_FILENAME."
exit 1
fi
}
# parse command line
if [ $# -eq 0 ]; then # must be at least one arg
echo "$USAGE" >&2
exit 1
fi
CHECKFILE=0
SLEEPTIME=0.5
BACKGROUND=''
while getopts "j:i:kc:s:o:hc:bc" OPT; do # "j:" waits for an argument "h" doesnt
case $OPT in
h) echo "$USAGE"
exit 0 ;;
j) MAX_NPROC=$OPTARG ;;
k) CHECKFILE=1 ;;
c) CFG_FILENAME=$OPTARG ;;
o) OUTDIR=$OPTARG ;;
s) SLEEPTIME=$OPTARG ;;
b) BACKGROUND='-background transparent ';;
i) INPUT_FILENAME=$OPTARG ;;
*) echo "$USAGE" >&2; exit 1; # Default.
esac
done
# Main program
echo Using $MAX_NPROC parallel threads
check_params
# Starte die Generierung
echo $INPUT_FILE
for i in `ls $INPUT_FILE| grep .svg `
do
ID=`echo "$i" | sed -e 's/;.*//g'`
PIC_NAME=`echo "$i" | sed -e 's/.svg//g'`
FILE_PATH=$INPUT_FILE/$PIC_NAME
if [ -s $FILE_PATH ] && [ $CHECKFILE -eq 1 ]
then
echo $PIC_NAME exists
else
RESIZE="${width}x${height}"
SWITCHES="-density $density $BACKGROUND$INPUT_FILE/$ID -resize $RESIZE $INPUT_FILE/$PIC_NAME.$format"
CMDLINE="$CMD $SWITCHES"
echo $CMDLINE
(echo $ID; $CMDLINE) &
while [ `ps -clx|grep -w 'convert' |wc -l` -ge $MAX_NPROC ]; do
sleep $SLEEPTIME
done
fi
done
wait # wait for all processes to finish before exit
if [ ! -z $OUTDIR ]; then
echo -n moving to $OUTDIR
if [ ! -d $OUTDIR ]; then
echo creating directory $OUTDIR
mkdir $OUTDIR
fi
mv $PICCREATE_OUT/*.jpg $OUTDIR
echo done
fi
# vi:set wrapmargin=0 ai showmatch ts=4 syntax=sh:
+2 -2
View File
@@ -1,5 +1,5 @@
#!/bin/bash
. setenv.sh
. "$(dirname "${BASH_SOURCE[0]}")/setenv.sh"
# Startet das Programm
python $PICCREATE_LIBS/piccreate.py "$@"
python3 $PICCREATE_LIBS/piccreate.py "$@"
+6
View File
@@ -0,0 +1,6 @@
@echo off
rem Startet das Programm
setlocal
call "%~dp0setenv.bat" || exit /b 1
"%PICCREATE_PYTHON%" "%PICCREATE_LIBS%\piccreate.py" %*
exit /b %errorlevel%
+38
View File
@@ -0,0 +1,38 @@
@echo off
rem ------------------
rem Umgebung fuer piccreate unter Windows setzen
rem (Gegenstueck zu setenv.sh, wird von den anderen .bat Dateien aufgerufen)
rem ------------------
rem Wo ist das Programm hinkopiert: das Verzeichnis ueber bin\
if not defined PICCREATE for %%I in ("%~dp0..") do set "PICCREATE=%%~fI"
echo setting environment to %PICCREATE%
set "PICCREATE_BIN=%PICCREATE%\bin"
set "PICCREATE_CFG=%PICCREATE%\cfg"
set "PICCREATE_LIBS=%PICCREATE%\libs"
set "PICCREATE_FONTS=%PICCREATE%\fonts"
set "PICCREATE_IN=%PICCREATE%\in"
set "PICCREATE_OUT=%PICCREATE%\out"
set "PICCREATE_BACKGROUNDS=%PICCREATE%\backgrounds"
set "PICCREATE_LOG=%PICCREATE%\log"
rem Wo sind die Pythonskripte ?
set "PYTHONPATH=%PICCREATE_LIBS%;%PYTHONPATH%"
rem Dateien und Ausgaben immer als UTF-8
set "PYTHONUTF8=1"
rem Welches Python: zuerst das aus .venv, dann der Python Launcher "py",
rem dann "python" aus dem PATH
if defined PICCREATE_PYTHON exit /b 0
if exist "%PICCREATE%\.venv\Scripts\python.exe" (
set "PICCREATE_PYTHON=%PICCREATE%\.venv\Scripts\python.exe"
exit /b 0
)
for /f "delims=" %%P in ('py -3 -c "import sys; print(sys.executable)" 2^>nul') do set "PICCREATE_PYTHON=%%P"
if defined PICCREATE_PYTHON exit /b 0
for /f "delims=" %%P in ('python -c "import sys; print(sys.executable)" 2^>nul') do set "PICCREATE_PYTHON=%%P"
if defined PICCREATE_PYTHON exit /b 0
echo Python 3 not found. Please install it from https://www.python.org
echo or with: winget install Python.Python.3.13
exit /b 1
+2 -1
View File
@@ -4,7 +4,8 @@
if [ ! -z $PICCREATE ];then
return 0
fi
export PICCREATE=~/develop/python/piccreate
# das Verzeichnis ueber bin/
export PICCREATE=$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/.." && pwd)
echo setting environment to $PICCREATE
# ------------------
+47
View File
@@ -0,0 +1,47 @@
@echo off
rem ================================================================
rem PICCREATE - Python Virtual Environment einrichten (Windows)
rem ================================================================
setlocal
rem nicht das Python aus einer alten .venv nehmen
set "PICCREATE_PYTHON="
if exist "%~dp0..\.venv\Scripts\python.exe" goto :installed
rem Python fuer das Anlegen der .venv suchen
for /f "delims=" %%P in ('py -3 -c "import sys; print(sys.executable)" 2^>nul') do set "PICCREATE_PYTHON=%%P"
if not defined PICCREATE_PYTHON for /f "delims=" %%P in ('python -c "import sys; print(sys.executable)" 2^>nul') do set "PICCREATE_PYTHON=%%P"
if not defined PICCREATE_PYTHON (
echo Python 3 nicht gefunden. Bitte installieren von https://www.python.org
echo oder mit: winget install Python.Python.3.13
exit /b 1
)
call "%~dp0setenv.bat" || exit /b 1
echo Initialisiere Python virtual environment...
"%PICCREATE_PYTHON%" -m venv "%PICCREATE%\.venv" --upgrade-deps
if errorlevel 1 (
rmdir /s /q "%PICCREATE%\.venv" 2>nul
echo Fehlgeschlagen.
exit /b 1
)
echo Erfolgreich.
echo Installiere erforderliche Python Packages...
"%PICCREATE%\.venv\Scripts\pip" install -r "%PICCREATE%\requirements.txt" -q || exit /b 1
echo Erfolgreich.
goto :browser
:installed
call "%~dp0setenv.bat" || exit /b 1
echo Erforderliche Python Packages bereits installiert!
:browser
rem Browser fuer webscan
if exist "%LOCALAPPDATA%\ms-playwright\chromium*" (
echo Playwright Browser bereits installiert!
exit /b 0
)
echo Installiere Playwright Browser...
"%PICCREATE%\.venv\Scripts\playwright" install chromium || exit /b 1
echo Erfolgreich.
exit /b 0
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# ================================================================
# PICCREATE - Python Virtual Environment einrichten
# ================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/setenv.sh"
if [ ! -d "$PICCREATE/.venv" ]; then
echo "Initialisiere Python virtual environment..."
python3 -m venv "$PICCREATE/.venv" --upgrade-deps || {
rm -rf "$PICCREATE/.venv"
echo "Fehlgeschlagen. Unter Ubuntu/Debian: sudo apt install python3-venv"
exit 1
}
echo "Erfolgreich."
source "$PICCREATE/.venv/bin/activate"
echo "Installiere erforderliche Python Packages..."
pip install -r "$PICCREATE/requirements.txt" -q || exit 1
echo "Erfolgreich."
deactivate
else
echo "Erforderliche Python Packages bereits installiert!"
fi
# Browser für webscan
# Arch: Playwright unterstützt Arch nicht offiziell -> Chromium der Distribution
if grep -qs '^ID=arch\|^ID_LIKE=.*arch' /etc/os-release; then
if [ -x /usr/bin/chromium ]; then
echo "Browser: /usr/bin/chromium"
echo "Für webscan setzen (z.B. in ~/.bashrc):"
echo " export PICCREATE_BROWSER=/usr/bin/chromium"
else
echo "Chromium fehlt. Bitte installieren:"
echo " sudo pacman -S chromium"
echo " export PICCREATE_BROWSER=/usr/bin/chromium"
fi
elif [ -n "$(ls -d ~/.cache/ms-playwright/chromium* 2>/dev/null)" ]; then
echo "Playwright Browser bereits installiert!"
else
echo "Installiere Playwright Browser..."
"$PICCREATE/.venv/bin/playwright" install chromium || exit 1
echo "Erfolgreich."
echo "Falls der Browser nicht startet (Ubuntu/Debian), Systembibliotheken installieren:"
echo " sudo $PICCREATE/.venv/bin/playwright install-deps chromium"
fi
+6
View File
@@ -0,0 +1,6 @@
@echo off
rem SVG Bilder mit ImageMagick in JPG/PNG umwandeln
setlocal
call "%~dp0setenv.bat" || exit /b 1
"%PICCREATE_PYTHON%" "%PICCREATE_LIBS%\svgconvert.py" %*
exit /b %errorlevel%
+4 -9
View File
@@ -1,11 +1,6 @@
#!/bin/bash
. setenv.sh
# Wo ist das Programm hinkopiert
if [ -z $PICCREATE ];then
echo please check environment
exit 0
fi
CMD=convert
SECTION_NAME=png
. "$(dirname "${BASH_SOURCE[0]}")/setenv.sh"
. $PICCREATE_BIN/generationlib
# SVG Bilder mit ImageMagick in JPG/PNG umwandeln
# svgconvert.sh -c punkteraetsel -p
python3 $PICCREATE_LIBS/svgconvert.py "$@"
+6
View File
@@ -0,0 +1,6 @@
@echo off
rem Vorschaubilder von Webseiten erzeugen (Playwright)
setlocal
call "%~dp0setenv.bat" || exit /b 1
"%PICCREATE_PYTHON%" "%PICCREATE_LIBS%\webscan.py" %*
exit /b %errorlevel%
Executable
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
. "$(dirname "${BASH_SOURCE[0]}")/setenv.sh"
# Vorschaubilder von Webseiten erzeugen (Playwright, Python-venv im Projekt)
PYTHON=python3
if [ -x $PICCREATE/.venv/bin/python ]; then
PYTHON=$PICCREATE/.venv/bin/python
fi
$PYTHON $PICCREATE_LIBS/webscan.py "$@"
+1
View File
@@ -42,4 +42,5 @@ align=left
width=1200
height=1200
format=jpg
density=200
+1 -1
View File
@@ -8,7 +8,7 @@
# width=190mm
height=898
width=2776
#background=Karo.png
background=Karo.png
# 0,352775 * pixellänge[bei 72dpi] = länge[mm]
# -> 2,83466799 pixel entsprechen 10 mm
# -> 1000 pixel mit 72dpi entsprechen 352,78 mm
+61
View File
@@ -0,0 +1,61 @@
# wie gross soll das Bild werden
[globals]
# angabe der Länge und Breite in pixelanzahl oder bei 72dpi
# -> 2,83466799 pixel entsprechen 1 mm bei 72 dpi
# -> 11,811116625 pixel entsprechen 1 mm bei 300 dpi
# Ziel für die Grösse:
# height=70mm
# width=190mm
height=898
width=2776
#background=Karo.png
# 0,352775 * pixellänge[bei 72dpi] = länge[mm]
# -> 2,83466799 pixel entsprechen 10 mm
# -> 1000 pixel mit 72dpi entsprechen 352,78 mm
# (wenn man diesselbe Länge bei 300dpi erreichen will, braucht man 4166pixel,
# das 4,16 fache )
# was soll wohin geschrieben werden (,..,..)
lines=PunkteName
# Die Überschriften der csv-Eingabedatei werden so nach intern übersetzt:
[translation]
customerid=Kennummer
name = Name
# das ist die TextZeile die auf das Bild kommt
[PunkteName]
# hier der Inhalt aus der Eingabedatei, der geschrieben werden soll
content=${name}
# Ab dem Punkt wird zu schreiben begonnen
x=-10
y=0
height=850
width=2640
# Fontname und Fonttyp
fontname=ausgangsschrift
glyph_space=20
counter_replace=True
#align=center,left,right
align=center
yalign=center
autoscale=True
[jpg]
width=2776
height=898
format=jpg
density=300
[png]
width=460
height=149
format=png
density=90
[webscan]
width=800
height=500
format=jpg
density=90
+328
View File
@@ -0,0 +1,328 @@
# piccreate
`piccreate` erzeugt aus einer CSV-Datei mit Namen für jede Zeile ein eigenes
SVG-Bild. Die Buchstaben stammen aus einem eigenen SVG-„Font“, dessen Glyphen
nummerierte Punkte enthalten. So entstehen z. B. personalisierte
**Punkt-zu-Punkt-Rätsel** und die passenden **Lösungsbilder**.
Beispiel für die CSV-Zeile `001;Silke`:
Rätsel (`cfg/punkteraetsel.cfg`, Font `ausgangsschrift`, Hintergrund `Karo.png`):
![Punkterätsel für "Silke"](images/punkteraetsel.png)
Lösung (`cfg/punkteloesung.cfg`, Font `ausgangsschrift_verbunden`, ohne Hintergrund):
![Lösung für "Silke"](images/punkteloesung.png)
Die Punkte werden über das ganze Wort fortlaufend nummeriert (1–32).
## Voraussetzungen
- Python 3 (für `piccreate.py`, `svgconvert.py` und `glyphextract.py` keine
weiteren Pakete nötig)
- ImageMagick (`magick`, ältere Versionen `convert`) für die Umwandlung
SVG → JPG/PNG mit `svgconvert.py`
- nur für Webseiten-Vorschauen (`webscan`): Playwright, siehe unten
Alles läuft unter Linux und Windows, siehe [Windows](#windows).
## Verzeichnisse
| Verzeichnis | Inhalt |
|----------------|-----------------------------------------------------------|
| `bin/` | Startskripte: `.sh` für Linux, `.bat` für Windows |
| `libs/` | Python-Programme `piccreate.py`, `svgconvert.py`, `webscan.py`, `glyphextract.py` |
| `cfg/` | Auftrags-Konfigurationen (`*.cfg`) |
| `in/` | Eingabedateien (`*.csv`) und Rohglyphen für neue Fonts |
| `out/` | Ausgabe der erzeugten Bilder |
| `fonts/` | Fonts: ein Verzeichnis pro Font mit Glyph-SVGs + `.cfg` |
| `backgrounds/` | Hintergrundbilder |
## Umgebung
Alle Pfade werden über Umgebungsvariablen gefunden, die `bin/setenv.sh` setzt
(`PICCREATE`, `PICCREATE_CFG`, `PICCREATE_IN`, `PICCREATE_OUT`,
`PICCREATE_FONTS`, `PICCREATE_BACKGROUNDS`, …). Das Projektverzeichnis ermittelt
`setenv.sh` selbst (das Verzeichnis über `bin/`), die Skripte in `bin/` lassen
sich deshalb von überall aufrufen. Nur wer die Variablen in der eigenen Shell
braucht, lädt `setenv.sh` selbst (bash oder zsh):
```sh
cd bin
. ./setenv.sh
```
## Gemeinsame Optionen
Alle Programme benutzen für dieselbe Aufgabe denselben Schalter:
| Option | Bedeutung | piccreate | svgconvert | webscan | glyphextract |
|---------------------|----------------------------------------------------|:-:|:-:|:-:|:-:|
| `-c`, `--config` | Konfiguration aus `cfg/` (Endung `.cfg` optional) | ✓ | ✓ | ✓ | |
| `-i`, `--infile` | Eingabe (CSV, URL-Liste, Glyph oder Font) | ✓ | ✓ | ✓ | ✓ |
| `-o`, `--outdir` | Ordner für die erzeugten Bilder (Standard `$PICCREATE_OUT`) | ✓ | ✓ | ✓ | ✓ |
| `--section` | Sektion der Konfiguration mit den Bildmaßen | | ✓ | ✓ | |
| `--format` | Bildformat `jpg` oder `png` (überschreibt die Konfiguration) | | ✓ | ✓ | |
| `-f`, `--force` | vorhandene Bilder überschreiben | | ✓ | ✓ | |
| `-j`, `--jobs` | Anzahl paralleler Jobs | | ✓ | ✓ | |
| `-t`, `--test` | Testbilder erzeugen | | ✓ | | |
| `-y`, `--dry` | Probelauf, keine Dateien schreiben | ✓ | ✓ | | |
| `-v`, `--verbose` | ausführliche Ausgabe | | ✓ | ✓ | |
| `-h`, `--help` | Hilfe | ✓ | ✓ | ✓ | ✓ |
| `--version` | Versionsnummer | ✓ | ✓ | ✓ | ✓ |
Ältere Namen funktionieren weiter: `--cfg` für `--config` und bei svgconvert
`--svgfolder` für `--outdir`.
## Bilder erzeugen
```sh
./piccreate -c punkteraetsel -i test
```
| Option | Bedeutung |
|-----------------------------|-----------|
| `-c`, `--config <datei>` | Konfiguration aus `cfg/` (Endung `.cfg` optional) |
| `-i`, `--infile <datei>` | CSV-Datei aus `in/` (Endung `.csv` optional) |
| `-o`, `--outdir <ordner>` | Ausgabeverzeichnis (Standard `$PICCREATE_OUT`) |
| `-s`, `--startfrom` | Nummerierung bei jedem Wort / Großbuchstaben neu beginnen |
| `-y`, `--dry` | SVG nur auf der Konsole ausgeben, keine Dateien schreiben |
Für jede CSV-Zeile entsteht `<customerid>.svg` im Ausgabeverzeichnis.
### Eingabedatei (CSV)
UTF-8, Trennzeichen `;`, `,` oder Tab (wird automatisch erkannt), erste Zeile
enthält die Spaltenüberschriften:
```
Kennummer;Name
001;Silke
002;Rau
```
## Konfiguration (`cfg/*.cfg`)
```ini
[globals]
# Bildgröße in Pixel
width=2776
height=898
# optional, Datei aus backgrounds/
background=Karo.png
# Textzeilen im Bild, durch Komma getrennt
lines=PunkteName
# interner Name = Spaltenüberschrift der CSV
[translation]
# Pflicht: ergibt den Dateinamen
customerid=Kennummer
name=Name
# eine Sektion je Eintrag in "lines"
[PunkteName]
# Text, Platzhalter aus [translation]
content=${name}
fontname=ausgangsschrift
# Startpunkt
x=-10
y=0
# Fläche für die Zeile, optional (sonst aus [globals])
width=2640
height=850
# Abstand zwischen den Buchstaben
glyph_space=20
# Punkte fortlaufend nummerieren
counter_replace=True
# left | center | right
align=center
# top | center | bottom
yalign=center
# Wort auf Bildgröße skalieren
autoscale=True
```
Statt `autoscale` kann auch fest mit `scale` bzw. `xscale`/`yscale` skaliert
werden. Fehlen in einer Zeilen-Sektion `width`/`height`, gelten die Werte aus
`[globals]`.
Die Sektionen `[jpgs]`, `[png]` usw. beschreiben die Rastergrafiken für
`svgconvert.py` (`width`, `height`, `format`, `density`).
## SVG in JPG/PNG umwandeln
```sh
./svgconvert.sh -c punkteraetsel -p
```
Wandelt alle SVGs in `$PICCREATE_OUT` (oder `-o <ordner>`) parallel mit
ImageMagick um; die Bilder landen im selben Ordner. Breite, Höhe, Format und
Auflösung kommen aus der Sektion `--section` der Konfiguration (Standard
`[png]`, für große JPGs z. B. `--section jpgs`).
| Option | Bedeutung |
|--------------------------------|-----------|
| `-c`, `--config <datei>` | Konfiguration aus `cfg/` (Endung `.cfg` optional) |
| `-o`, `--outdir <ordner>` | Ordner mit den SVGs, auch für die Bilder (Standard `$PICCREATE_OUT`) |
| `--section <name>` | Sektion mit den Bildmaßen (Standard `png`) |
| `--format <jpg\|png>` | Bildformat, überschreibt die Konfiguration |
| `-i`, `--infile <datei>` | CSV aus `in/` prüfen: fehlende Bilder melden, nur diese umwandeln |
| `-j`, `--jobs <n>` | Anzahl paralleler Jobs (Standard 8) |
| `-b`, `--background <farbe>` | Hintergrundfarbe, `none` für transparent |
| `-f`, `--force` | vorhandene Bilder überschreiben |
| `-p`, `--progress` | Fortschrittsbalken anzeigen |
| `-v`, `--verbose` | ausführliche Ausgabe |
| `-t`, `--test <n>` | `n` Test-SVGs erzeugen und umwandeln |
| `-y`, `--dry` | Probelauf ohne Umwandlung |
## Vorschaubilder von Webseiten (webscan)
`libs/webscan.py` lädt für jede Zeile einer Eingabedatei eine Webseite in
einem Browser ohne Fenster und speichert den sichtbaren Bereich als Bild
`<Kundennummer>.jpg`. Die Größe kommt aus der Sektion `[webscan]` der
Konfiguration (in `cfg/webscan.cfg`: 800×500).
Einmalig einrichten mit `bin/setup-venv.sh`: legt die Python-Umgebung `.venv`
im Projektverzeichnis an, installiert `requirements.txt` und kümmert sich um
den Browser (Ubuntu: Playwright-Chromium, Arch: Hinweis auf das Chromium der
Distribution). Ist `.venv` schon vorhanden, wird nichts neu installiert.
```sh
./setup-venv.sh
```
Von Hand geht es so:
```sh
python3 -m venv .venv # Ubuntu: vorher sudo apt install python3-venv
.venv/bin/pip install -r requirements.txt
```
Browser unter Ubuntu/Debian:
```sh
sudo .venv/bin/playwright install-deps chromium
.venv/bin/playwright install chromium
```
Browser unter Arch Linux (Playwright unterstützt Arch nicht offiziell, daher
besser das Chromium der Distribution):
```sh
sudo pacman -S chromium
export PICCREATE_BROWSER=/usr/bin/chromium # oder Option --browser /usr/bin/chromium
```
Fehlen Playwright oder der Browser, gibt `webscan.py` die passenden Befehle
für die erkannte Distribution aus und bricht mit Exit-Code 2 ab.
Eingabedatei, z. B. `in/kunden.csv` (Kopfzeile optional, `http://` wird bei
Bedarf ergänzt):
```
Kennummer;URL
1001;www.example.org
1002;https://www.example.com/start
```
Aufruf:
```sh
./webscan.sh -c webscan -i kunden
```
| Option | Bedeutung |
|-----------------------------|-----------|
| `-c`, `--config <datei>` | Konfiguration aus `cfg/` (Standard `webscan`) |
| `-i`, `--infile <datei>` | Eingabedatei aus `in/` oder mit Pfad |
| `-o`, `--outdir <ordner>` | Ausgabeverzeichnis (Standard `$PICCREATE_OUT`) |
| `--section <name>` | Sektion mit den Bildmaßen (Standard `webscan`) |
| `--format <jpg\|png>` | Bildformat, überschreibt die Konfiguration |
| `-j`, `--jobs <n>` | Anzahl gleichzeitig geladener Seiten (Standard 4) |
| `--timeout <sekunden>` | Wartezeit pro Seite (Standard 20) |
| `-f`, `--force` | vorhandene Bilder überschreiben |
| `--browser <pfad>` | eigenes Chromium statt des Playwright-Browsers (Standard `$PICCREATE_BROWSER`) |
| `-v`, `--verbose` | ausführliche Ausgabe |
Seiten ohne URL oder mit Ladefehler werden übersprungen und am Ende
aufgelistet; der Exit-Code ist dann 1.
## Fonts
Ein Font liegt in `fonts/<name>/` und besteht aus
- einer SVG-Vorlage pro Zeichen (z. B. `a.svg`), eingeschlossen in
`<g transform="translate(${posx},${posy})">…</g>`; die Punktnummern sind
Platzhalter `${x1}`, `${x2}`, …
- der Datei `<name>.cfg` mit einer Sektion pro Zeichen:
```ini
[a]
# Breite des Zeichens
XSTEP=50.071972
# Höhe des Zeichens
YMAX=139.04482
# Anzahl der Platzhalter x1..xn
NPATTERN=11
# Vorlagendatei
FONTNAME=a.svg
```
Neue Fonts entstehen aus in Inkscape gezeichneten Glyphen (Punkte als Kreise,
Nummern als Text `x1`, `x2`, …):
```sh
./extract-font.sh ausgangsschrift # Glyphen + ausgangsschrift.cfg erzeugen
./fonttest.sh ausgangsschrift # Testbilder der einzelnen Glyphen erzeugen
./extract-glyph.sh -i ../in/ausgangsschrift/a.svg -e # nur ein Buchstabe
```
Alle drei rufen `libs/glyphextract.py` auf. Dort ist `-i` ein einzelner
Buchstabe oder ein Font (Ordner in `in/` oder Pfad); mit `-e` werden die
Glyphen für den Font extrahiert (bei einem Font zusätzlich `<name>.cfg`),
ohne `-e` entstehen Testbilder zum Prüfen in Inkscape:
```sh
python3 glyphextract.py -i ausgangsschrift -e # = extract-font.sh
python3 glyphextract.py -i ausgangsschrift # = fonttest.sh
python3 glyphextract.py -i ../in/ausgangsschrift/a.svg -e
```
| Option | Bedeutung |
|----------------------------------|-----------|
| `-i`, `--infile <datei\|font>` | ein Buchstabe (`.svg`) oder ein Font (Ordner in `in/` oder Pfad) |
| `-e`, `--extract` | Glyphen für den Font extrahieren, bei einem Font zusätzlich `<name>.cfg`; ohne `-e` Testbilder |
| `-o`, `--outdir <ordner>` | Ausgabeverzeichnis (Standard `$PICCREATE_OUT`) |
Alle schreiben nach `$PICCREATE_OUT` (oder `-o <ordner>`). Das Ergebnis
(Glyph-SVGs und `<name>.cfg`) wird anschließend nach `fonts/<name>/`
übernommen. Werte wie `XSTEP` kann man danach in der `.cfg` noch von Hand
anpassen.
## Windows
Für jedes Shell-Skript in `bin/` gibt es eine `.bat`-Datei mit denselben
Optionen: `piccreate.bat`, `svgconvert.bat`, `webscan.bat`,
`extract-glyph.bat`, `extract-font.bat`, `fonttest.bat`, `setup-venv.bat`.
`setenv.bat` setzt die Umgebung; das Projektverzeichnis ermittelt es selbst
(das Verzeichnis über `bin\`), ein fester Pfad ist nicht nötig.
Einrichten:
1. Python 3 installieren (https://www.python.org oder
`winget install Python.Python.3.13`)
2. ImageMagick installieren (https://imagemagick.org), nur für `svgconvert`
3. nur für `webscan`: `bin\setup-venv.bat` ausführen (legt `.venv` an und
installiert Playwright mit Chromium)
Beispiel:
```bat
cd bin
piccreate.bat -c punkteraetsel -i test
svgconvert.bat -c punkteraetsel -p
```
Die `.bat`-Dateien nehmen das Python aus `.venv`, falls vorhanden, sonst den
Python Launcher `py` oder `python` aus dem `PATH`.
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

+3 -2
View File
@@ -1,6 +1,7 @@
Kennummer;Name
001;Au
001;Silke
002;Rau
003;Lux
004;May
005;Johnson-Kronh-Wattenberg
005;Deutsche
006;Silverkropp
1 Kennummer Name
2 001 Au Silke
3 002 Rau
4 003 Lux
5 004 May
6 005 Johnson-Kronh-Wattenberg Deutsche
7 006 Silverkropp
+243
View File
@@ -0,0 +1,243 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" erzeugt aus in Inkscape gezeichneten Buchstaben (SVG) die Glyphen und die
Fontconfig für piccreate (Ersatz für extract-glyph.sh, extract-font.sh und
fonttest.sh, läuft auch unter Windows).
Die Auswahl der Zeilen folgt bewusst den früheren grep/sed Aufrufen, damit
die erzeugten Glyphen gleich bleiben.
"""
__version__ = '0.1.0'
__license__ = 'BSD'
import glob
import locale
import optparse
import os
import re
import sys
from decimal import Decimal, InvalidOperation
# Wortgrenzen wie \< und \> bei grep
WB = r'(?<!\w)(?=\w)'
WE = r'(?<=\w)(?!\w)'
LINE_IN = re.compile('<' + WB + 'line' + WE + '|x1|x2|y1|y2|/>|style')
LINE_OUT = re.compile(WB + 'text' + WE + '|id=|http:')
CIRCLE_IN = re.compile('circle|translate|' + WB + 'r' + WE)
TEXT_IN = re.compile('<' + WB + 'text|text' + WE + '>|' + WB + 'x' + WE + '=|'
+ WB + 'y' + WE + '=|tspan|style|preserve')
TEXT_OUT = re.compile(WB + 'transform-center-x' + WE + '|' + WB + 'window-|'
+ WB + 'id' + WE + '=')
CENTER_X = re.compile(r'inkscape:transform-center-x=".*"')
CENTER_Y = re.compile(r'inkscape:transform-center-y=".*"')
PATTERN = re.compile(r'>(x[0-9]+)<')
NUMBER = re.compile(r'\s*[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?')
TEST_BEGIN = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">'
TEST_END = '</svg>'
GLYPH_BEGIN = '<g transform="translate(${posx},${posy})">'
GLYPH_END = '</g>'
def split_lines(text):
''' zerlegt einen Text in Zeilen (wie grep, ohne Zeilenende) '''
lines = text.split('\n')
if lines and lines[-1] == '':
lines.pop()
return lines
def strip_center(line):
line = CENTER_X.sub('', line)
return CENTER_Y.sub('', line)
def number_key(value):
''' Sortierschlüssel wie bei "sort -g": Zeilen ohne Zahl zuerst '''
m = NUMBER.match(value)
if not m:
return (0, 0.0, value)
return (1, float(m.group(0)), value)
def coordinates(glyph_lines, axis):
''' sammelt alle x- bzw. y-Koordinaten aus Texten, Kreisen und Linien '''
values = []
if axis == 'x':
att = re.compile(WB + 'x' + WE + '=')
else:
att = re.compile('y=')
for line in glyph_lines:
if att.search(line):
values.append(re.sub(r'^\s+', '', line).replace(axis + '=', '').replace('"', ''))
# Kreise: transform="translate(x,y)"
for line in glyph_lines:
if '<g' not in line and 'transform=' in line:
value = re.sub(r'^\s+', '', line).replace('translate', '').replace('transform=', '')
value = re.sub(r'["|()]', '', value)
if axis == 'x':
value = re.sub(r',.*', '', value)
else:
value = re.sub(r'.*,', '', value)
values.append(value)
# Linien: x1/x2 bzw. y1/y2
for name in (axis + '1=', axis + '2='):
for line in glyph_lines:
if name in line:
values.append(re.sub(r'^\s+', '', line).replace(name, '').replace('"', ''))
values = sorted(set(values), key=number_key)
if not values:
return '', ''
return values[0], values[-1]
def difference(maximum, minimum):
''' maximum - minimum, formatiert wie bc '''
try:
a, b = Decimal(maximum), Decimal(minimum)
except InvalidOperation:
return ''
scale = max(-a.as_tuple().exponent, -b.as_tuple().exponent, 0)
result = (a - b).quantize(Decimal(1).scaleb(-scale))
if result == 0:
return '0'
text = format(result, 'f')
return text.replace('-0.', '-.') if text.startswith('-0.') else re.sub(r'^0\.', '.', text)
def extract_glyph(svg_path, extract=True):
''' liefert (Glyph-SVG, Fontconfig-Sektion) für einen Buchstaben '''
with open(svg_path, encoding='utf-8', newline='') as fh:
source = split_lines(fh.read())
lines = [strip_center(l) for l in source if LINE_IN.search(l) and not LINE_OUT.search(l)]
circles = [strip_center(l) for l in source if CIRCLE_IN.search(l)]
texts = [strip_center(l) for l in source if TEXT_IN.search(l) and not TEXT_OUT.search(l)]
if extract:
glyph = [GLYPH_BEGIN] + texts + circles + lines + [GLYPH_END]
else:
glyph = [TEST_BEGIN] + texts + circles + lines + [TEST_END]
xmin, xmax = coordinates(glyph, 'x')
ymin, ymax = coordinates(glyph, 'y')
# Anzahl der Platzhalter x1..xn, dann durch Templatezeichen ${x1} ersetzen
npattern = sum(1 for l in glyph if re.search(r'>x[0-9]+<', l))
glyph = [PATTERN.sub(r'>${\1}<', l) for l in glyph]
base = os.path.splitext(os.path.basename(svg_path))[0]
config = [
'[' + base + ']',
'XSTEP=' + difference(xmax, xmin),
'XMIN=' + xmin,
'XMAX=' + xmax,
'YSTEP=' + difference(ymax, ymin),
'YMIN=' + ymin,
'YMAX=' + ymax,
'NPATTERN=' + str(npattern),
'FONTNAME=' + base + '.svg',
'#',
]
return '\n'.join(glyph) + '\n', '\n'.join(config) + '\n'
def write_glyph(svg_path, out_dir, extract=True):
''' schreibt Glyph und Fontconfig-Sektion ins Ausgabeverzeichnis '''
glyph, config = extract_glyph(svg_path, extract)
base = os.path.splitext(os.path.basename(svg_path))[0]
with open(os.path.join(out_dir, base + '.svg'), 'w', encoding='utf-8', newline='\n') as fh:
fh.write(glyph)
return base, config
def font_source(name):
''' sucht das Verzeichnis mit den Buchstaben eines Fonts '''
in_dir = os.path.join(os.environ.get('PICCREATE_IN', ''), name)
if os.path.isdir(in_dir):
return in_dir
if os.path.isdir(name):
return name
raise SystemExit('"' + name + '" is no directory. Please give a directory with .svg glyphs as input')
def sorted_glyphs(source):
''' alle .svg Dateien, sortiert wie die Shell (a A ä b B ...) '''
try:
locale.setlocale(locale.LC_COLLATE, '')
except locale.Error:
pass
return sorted(glob.glob(os.path.join(source, '*.svg')), key=locale.strxfrm)
def main():
out_dir = options.outdir
if not os.path.isdir(out_dir):
print('"' + out_dir + '" does not exist. Please give the directory for the output!')
return 1
if os.path.isfile(options.infile):
# ein Buchstabe (extract-glyph.sh)
if not os.path.getsize(options.infile):
print('input file is empty')
return 1
base, config = write_glyph(options.infile, out_dir, options.extract)
with open(os.path.join(out_dir, base + '.cfg'), 'w', encoding='utf-8', newline='\n') as fh:
fh.write(config)
return 0
# alle Buchstaben eines Fonts
source = font_source(options.infile)
configs = []
for svg_path in sorted_glyphs(source):
base, config = write_glyph(svg_path, out_dir, options.extract)
print(base)
configs.append((base, config))
if options.extract:
# Font (extract-font.sh): alle Sektionen in <font>.cfg
cfg_name = os.path.basename(os.path.normpath(source)) + '.cfg'
with open(os.path.join(out_dir, cfg_name), 'w', encoding='utf-8', newline='\n') as fh:
fh.write(''.join(config for _, config in configs))
print('->', os.path.join(out_dir, cfg_name))
else:
# Testbilder (fonttest.sh): je Buchstabe eine .cfg daneben
for base, config in configs:
with open(os.path.join(out_dir, base + '.cfg'), 'w', encoding='utf-8', newline='\n') as fh:
fh.write(config)
return 0
if __name__ == '__main__':
parser = optparse.OptionParser(usage="""%prog -i <glyph.svg | font> [-e] [options]
Example usage
-------------
one glyph, test picture to check it with inkscape:
$ %prog -i ../in/ausgangsschrift/a.svg
one glyph, extract the points and text for the font:
$ %prog -i ../in/ausgangsschrift/a.svg -e
all glyphs of a font (directory in in/ or a path) including the font config:
$ %prog -i ausgangsschrift -e
test pictures of all glyphs of a font:
$ %prog -i ausgangsschrift
""", version='%prog ' + __version__)
parser.add_option("-i", "--infile", dest="infile",
help="svg file of one glyph or directory with the glyphs of a font")
parser.add_option("-e", "--extract", dest="extract", action='store_true', default=False,
help="extract the glyphs for the font (and write <font>.cfg) "
"instead of test pictures")
parser.add_option("-o", "--outdir", dest="outdir",
default=os.environ.get('PICCREATE_OUT', '.'),
help="directory where the files will be written. default is $PICCREATE_OUT")
(options, args) = parser.parse_args()
if not options.infile:
parser.error('please give a glyph or a font with -i')
sys.exit(main())
# vim: set tw=80 ft=python ts=4 sw=4:
+41 -41
View File
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__version__ = '0.0.1'
@@ -9,9 +9,9 @@ __license__ = 'BSD'
import optparse
import os
import re
import ConfigParser
import configparser
import pathlib
from utils import CSVImport, CompleteEnding, get_translation
from django.utils.encoding import smart_str, smart_unicode
from string import Template
class PicError(Exception):
@@ -40,14 +40,15 @@ def create_picture_frame( config ):
try:
params['background'] = config.get('globals', 'background')
imagetpl = ''' <image width="${width}" height="${height}"
xlink:href="file://${background}">
xlink:href="${background}">
</image>
'''
background = config.get('globals', 'background')
font_path = os.path.expandvars(os.path.join('$PICCREATE_BACKGROUNDS', background ))
params['background'] = os.path.normpath(font_path)
# als file:// URL, damit es auch mit Windows-Pfaden (C:\...) klappt
params['background'] = pathlib.Path(os.path.abspath(font_path)).as_uri()
svgbegin = svgbegin + Template( imagetpl ).substitute(params)
except ConfigParser.NoOptionError:
except configparser.NoOptionError:
# dann eben kein Hintergrundbild
pass
svgend = '</svg>'
@@ -60,18 +61,18 @@ def write_font_line( content, font, params ):
wordsvg = w.GetSVG()
# falls das Wort nun skaliert werden soll
if params.has_key('scale'):
if 'scale' in params:
params['xscale'] = params['scale']
params['yscale'] = params['scale']
if not params.has_key('xscale'):
if 'xscale' not in params:
params['xscale'] = 1.0
if not params.has_key('yscale'):
if 'yscale' not in params:
params['yscale'] = 1.0
pic_width = float(params['width'])
pic_height = float(params['height'])
if params.has_key('autoscale'):
if 'autoscale' in params:
wordlength = w.calc_length()
wordheight = w.calc_height()
#print wordlength, wordheight
@@ -87,7 +88,7 @@ def write_font_line( content, font, params ):
scale_faktor = 1.0
# berechne immer den richtigen Startpunkt, je nach Bündigkeit
if params.has_key('align'):
if 'align' in params:
xstart = float(params['x'])
if params['align'] == 'left':
x = xstart
@@ -101,7 +102,7 @@ def write_font_line( content, font, params ):
else:
params['xstart'] = params['x']
if params.has_key('yalign'):
if 'yalign' in params:
ystart = float(params['y'])
if params['yalign'] == 'top':
y = 0.0
@@ -109,6 +110,7 @@ def write_font_line( content, font, params ):
word_height = w.calc_height()
y = ystart * scale_faktor + (pic_height - word_height * scale_faktor)/2.0
elif params['yalign'] == 'bottom':
word_height = w.calc_height()
y = ystart * scale_faktor + pic_height - word_height * scale_faktor
else:
y = ystart
@@ -174,7 +176,6 @@ class Word:
if params['counter_replace']:
xn = g.GetAttribute('npattern')
for i in range(1, int(xn)+1):
counter = counter + 1
content['x'+str(i)] = counter
svgstr = svgstr + g.GetSVG(content)
@@ -205,9 +206,9 @@ class Font:
self.name = fontname
self.path = font_path
self.glyphs = dict()
fontconfig = ConfigParser.ConfigParser()
fontconfig = configparser.ConfigParser(interpolation=None, strict=False)
config_path = os.path.join(font_path, fontname+'.cfg')
fontconfig.read(config_path)
fontconfig.read(config_path, encoding='utf-8')
fontconfig.optionxform = str
# lies alle Zeichen des Fonts ein
@@ -215,8 +216,8 @@ class Font:
# hol die Fontattribute wie Ausdehnung usw.
content = dict(fontconfig.items(letter))
# lade das svg template dazu ein
glyph_path = os.path.join( font_path, smart_unicode(content['fontname']))
f = open(glyph_path, 'r')
glyph_path = os.path.join( font_path, content['fontname'])
f = open(glyph_path, 'r', encoding='utf-8')
svgstr = f.read()
f.close()
# und bau das Zeichen auf
@@ -225,7 +226,7 @@ class Font:
def __repr__(self):
return repr(self.glyphs)
def GlyphGet(self, glyphname):
return self.glyphs[smart_str(glyphname)]
return self.glyphs[glyphname]
def init_fonts( config ):
''' lädt alle benötigten Fonts für den Auftrag in den Speicher
@@ -288,13 +289,13 @@ def create_picture(config, fontinfos, data, pic_path):
lineparams = dict(config.items(linename))
# falls nix angegeben, dann aus hol defaults aus globalen daten
if not lineparams.has_key('width'):
if 'width' not in lineparams:
lineparams['width'] = config.get('globals', 'width')
if not lineparams.has_key('height'):
if 'height' not in lineparams:
lineparams['height'] = config.get('globals', 'height')
if not lineparams.has_key('x'):
if 'x' not in lineparams:
lineparams['x'] = 0.0
if not lineparams.has_key('y'):
if 'y' not in lineparams:
lineparams['y'] = 0.0
picstr = write_font_line( content, font, lineparams )
@@ -303,14 +304,14 @@ def create_picture(config, fontinfos, data, pic_path):
randstr = write_random_font_line( content, font, lineparams)
if randstr: svgstr = svgstr + randstr
svgstr = svgstr + picend
svgstr = svgstr + picend
if options.dry:
print svgstr
else:
f = open(pic_path, 'w')
f.write( svgstr )
f.close()
if options.dry:
print(svgstr)
else:
f = open(pic_path, 'w', encoding='utf-8')
f.write( svgstr )
f.close()
def main( config ):
@@ -327,17 +328,17 @@ def main( config ):
config_path = os.path.expandvars(os.path.join('$PICCREATE_CFG', CompleteEnding( config, 'cfg')))
in_path = os.path.expandvars(os.path.join('$PICCREATE_IN', CompleteEnding( infile, 'csv')))
if not os.path.exists(config_path):
print '\"'+ config_path +'\" does not exist. Please give the name of a config file!'
print('\"'+ config_path +'\" does not exist. Please give the name of a config file!')
return None
if not os.path.exists(in_path):
print '\"'+ in_path +'\" does not exist. Please give the name to a input file!'
print('\"'+ in_path +'\" does not exist. Please give the name to a input file!')
return None
if not os.path.exists(out_path):
print '\"'+ out_path +'\" does not exist. Please give the path to the directory for the pictures!'
print('\"'+ out_path +'\" does not exist. Please give the path to the directory for the pictures!')
return None
config = ConfigParser.RawConfigParser()
config.read(config_path)
config = configparser.RawConfigParser(strict=False)
config.read(config_path, encoding='utf-8')
Trans = get_translation(config_path, 'translation')
Rows = CSVImport(in_path, translate=Trans)
@@ -345,12 +346,11 @@ def main( config ):
fontinfos = init_fonts( config )
for row in Rows:
filename = row.customerid
print filename
print(filename)
content = row.__dict__
del content['firstrow']
del content['row']
pic_path = os.path.join(out_path, filename+'.svg')
create_picture(config, fontinfos, content, pic_path)
@@ -370,20 +370,20 @@ Read Confg, create wakeup and scale this kind of jiffy box
""", version='%prog ' + __version__)
parser.add_option("-c", "--cfg", dest="config", default="test",
help="name of the config file with the Name of the rows to be used.",
parser.add_option("-c", "--config", "--cfg", dest="config", default="test",
help="name of the config file in cfg/ with the lines to be written",
metavar="CONFIG_FILE")
parser.add_option("-i", "--infile", dest="infile",
help="csv file with input of names/content",
action='store')
parser.add_option("-s", "--startfrom", dest="startfrom",
help="csv file with input of names/content",
help="start the numbering again at every word / capital letter",
action='store_true')
parser.add_option("-o", "--outdir", dest="outdir", default=os.path.expandvars('$PICCREATE_OUT'),
help="path to the directory where the maps will be generated",
help="directory where the pictures will be written. default is $PICCREATE_OUT",
action='store')
parser.add_option("-y", "--dry", dest="dry",
help="dont generate images. Just ask the coordinates and fill database.", action='store_true' )
help="dont write files, print the svg instead", action='store_true' )
(options, args) = parser.parse_args()
+200
View File
@@ -0,0 +1,200 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# A Python Library to create a Progress Bar.
# Copyright (C) 2008 BJ Dierkes <wdierkes@5dollarwhitebox.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
# This class is an improvement from the original found at:
#
# http://code.activestate.com/recipes/168639/
#
import sys,os
class ProgressBar:
def __init__(self, min_value = 0, max_value = 100, width=77,**kwargs):
self.char = kwargs.get('char', '#')
self.mode = kwargs.get('mode', 'dynamic') # fixed or dynamic
if not self.mode in ['fixed', 'dynamic']:
self.mode = 'fixed'
self.bar = ''
self.min = min_value
self.max = max_value
self.span = max_value - min_value
self.width = width
self.amount = 0 # When amount == max, we are 100% done
self.update_amount(0)
def increment_amount(self, add_amount = 1):
"""
Increment self.amount by 'add_ammount' or default to incrementing
by 1, and then rebuild the bar string.
"""
new_amount = self.amount + add_amount
if new_amount < self.min: new_amount = self.min
if new_amount > self.max: new_amount = self.max
self.amount = new_amount
self.build_bar()
def update_amount(self, new_amount = None):
"""
Update self.amount with 'new_amount', and then rebuild the bar
string.
"""
if not new_amount: new_amount = self.amount
if new_amount < self.min: new_amount = self.min
if new_amount > self.max: new_amount = self.max
self.amount = new_amount
self.build_bar()
def build_bar(self):
"""
Figure new percent complete, and rebuild the bar string base on
self.amount.
"""
diff = float(self.amount - self.min)
percent_done = int(round((diff / float(self.span)) * 100.0))
# figure the proper number of 'character' make up the bar
all_full = self.width - 2
num_hashes = int(round((percent_done * all_full) / 100))
if self.mode == 'dynamic':
# build a progress bar with self.char (to create a dynamic bar
# where the percent string moves along with the bar progress.
self.bar = self.char * num_hashes
else:
# build a progress bar with self.char and spaces (to create a
# fixe bar (the percent string doesn't move)
self.bar = self.char * num_hashes + ' ' * (all_full-num_hashes)
percent_str = str(percent_done) + "%"
self.bar = '[ ' + self.bar + ' ] ' + percent_str
def __str__(self):
return str(self.bar)
def bufcount(filename):
if not os.path.exists(filename):
raise Exception
try:
fh = open(filename, encoding='utf-8', errors='replace')
lines = 0
buf_size = 1024 * 1024
read_f = fh.read # loop optimization
buf = read_f(buf_size)
while buf:
lines += buf.count('\n')
buf = read_f(buf_size)
finally:
fh.close()
return lines
class EasyProgressBar():
def __init__(self, filename=None, array=None, show=False):
if filename:
NumElems = bufcount(filename)-1
if array:
NumElems = len(array)
if not array and not filename:
raise Exception
# initialisiere Progressbar, wenn gewünscht
if show:
# zaehle alle Zeilen des Files, das verwendet wird
# die erste Zeile enthält dabei die Ueberschriften der Spalten
limit = NumElems-1
self.prog = ProgressBar(0, limit, 77, mode='dynamic', char='-')
self.oldprog = str(self.prog)
else:
self.prog = False
def slide_progbar(self):
if self.prog:
prog = self.prog
oldprog = self.oldprog
prog.increment_amount()
if oldprog != str(prog):
print(prog, "\r", end=' ')
sys.stdout.flush()
oldprog=str(prog)
def init_progbar(filename=None, array=None, init=False):
""" Initialisere die Progressbar
"""
from progressbar import ProgressBar, bufcount
if filename:
NumElems = bufcount(filename)-1
if array:
NumElems = len(array)
# initialisiere Progressbar, wenn gewünscht
if init:
# zaehle alle Zeilen des Files, das verwendet wird
# die erste Zeile enthält dabei die Ueberschriften der Spalten
limit = NumElems-1
prog = ProgressBar(0, limit, 77, mode='dynamic', char='-')
oldprog = str(prog)
return (prog, oldprog)
else:
return (None, None)
def slide_progbar(prog, oldprog, init):
""" verschiebe die Progressbar
"""
if init:
prog.increment_amount()
if oldprog != str(prog):
print(prog, "\r", end=' ')
sys.stdout.flush()
oldprog=str(prog)
def main():
print()
limit = 1000000
print('Example 1: Fixed Bar')
prog = ProgressBar(0, limit, 77, mode='fixed')
oldprog = str(prog)
for i in range(limit+1):
prog.update_amount(i)
if oldprog != str(prog):
print(prog, "\r", end=' ')
sys.stdout.flush()
oldprog=str(prog)
print('\n\n')
print('Example 2: Dynamic Bar')
prog = ProgressBar(0, limit, 77, mode='dynamic', char='-')
oldprog = str(prog)
for i in range(limit+1):
prog.increment_amount()
if oldprog != str(prog):
print(prog, "\r", end=' ')
sys.stdout.flush()
oldprog=str(prog)
print('\n\n')
if __name__ == '__main__':
main()
+267
View File
@@ -0,0 +1,267 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__version__ = "0.0.1"
__author__ = "Kamil Bugajski (kamil@bugajski.de)"
__copyright__ = "Copyright 2012, Kamil bugajski"
__license__ = "BSD"
import os
import cProfile
import optparse
import configparser
import re
import shutil
import subprocess
import csv
from string import Template
from concurrent import futures
from progressbar import EasyProgressBar
from utils import CompleteEnding
# ImageMagick 7 heisst "magick"; unter Windows ist "convert" ein Systemprogramm
IMAGEMAGICK = shutil.which('magick') or shutil.which('convert')
def get_translation(cfg_path, sec_name):
""" lädt die Zuordnung zwischen einem .csv Eingabefile und den Attributen"""
config = configparser.RawConfigParser()
config.read(cfg_path, encoding='utf-8')
trans = dict(config.items(sec_name))
return dict([[v,k] for k,v in trans.items()])
def getfilelist(path):
""" Receives a path and returns a list with all svg-files in this path."""
svgnames = []
allfiles = os.listdir(path)
for i in allfiles:
if re.findall('.svg', i):
svgnames.append(os.path.splitext(i)[0])
return svgnames
def convertSVG(in_path,filename,pic_config):
"""Create a svg-file for conversion-testing"""
inpath = os.path.join(in_path,str(filename)+".svg")
jpgpath = os.path.join(in_path,str(filename)+"." + pic_config['format'])
if (not options.force) and os.path.isfile(jpgpath):
if options.verbose:
print (filename, 'already exist!')
else:
cmdline = [IMAGEMAGICK,
'-density', pic_config['density'],
'-background', pic_config['background'],
inpath,
'-resize', pic_config['width'] + "x" + pic_config['height'],
jpgpath]
if not options.dry:
subprocess.check_call(cmdline)
if options.verbose:
print ('start converting', filename)
Epb.slide_progbar()
def startThreading(filelist,pic_config,work_path):
"""Start the threads for the conversion"""
if not filelist:
print ('filelist is empty - nothing to convert')
return True
executor = futures.ThreadPoolExecutor(max_workers = int(options.jobs))
global Epb
Epb = EasyProgressBar(array = filelist, show=options.progress)
for svg in filelist:
executor.submit(convertSVG,work_path,svg,pic_config)
executor.shutdown(wait=True)
def createTestFiles(in_path,filenumber):
"""Create a svg-file for conversion-testing"""
if options.verbose:
print ('creating:',filenumber)
path = os.path.join(in_path,str(filenumber)+".svg")
svgfile = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="531.49603"
height="531.49603"
id="svg2">
<defs
id="defs4" />
<g
transform="translate(0,-520.86597)"
id="layer1">
<text
x="174.28572"
y="806.64789"
id="text2985"
xml:space="preserve"
style="font-size:72px;font-style:normal;font-weight:normal;line-height:\
125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;\
stroke:none;font-family:Sans">
<tspan
x="174.28572"
y="806.64789"
id="tspan2987"
style="font-size:72px;font-style:normal;font-variant:normal;\
font-weight:normal;font-stretch:normal;font-family:Ubuntu;\
-inkscape-font-specification:Ubuntu">$sub</tspan></text>
</g>
</svg>
'''
s = Template(svgfile)
f = open(path,'w')
writefile = s.substitute(sub = filenumber)
f.write(writefile)
f.close
def main():
"""get all information from the switches an start the parallel conversion"""
if not IMAGEMAGICK and not options.dry:
print('ImageMagick not found. Please install it (https://imagemagick.org).')
return None
#set the workpath and the default config
work_path = os.path.expandvars(options.outdir)
if not os.path.exists(work_path):
print('\"'+ work_path +'\" does not exist. Please give the name of the folder with the svg-files!')
return None
pic_config = {
'width' : '600',
'height' : '200',
'format' : options.format or 'jpg',
'density' : '150',
'background' : 'white',
'folder' : work_path,
'customerid' : 'customerid'
}
if options.test:
filelist = []
if options.verbose:
print ('---- Start of test-sequence ----')
print ('---- creating testfiles: ----')
for filenumber in range(0, int(options.test),1):
createTestFiles(work_path,filenumber)
filelist.append(filenumber)
if options.verbose:
print ('---- starting the conversion: ----')
startThreading(filelist,pic_config,work_path)
if options.verbose:
print ('---- test completed ----')
return True
filelist = getfilelist(work_path)
#load and override the default config if we receive an config file
if options.config:
config_path = os.path.expandvars(os.path.join('$PICCREATE_CFG', CompleteEnding(options.config, 'cfg')))
if not os.path.exists(config_path):
print('\"'+ config_path +'\" does not exist. Please give the name of a config file!')
return None
config = configparser.RawConfigParser()
config.read(config_path, encoding='utf-8')
# fill the config
if not config.has_section(options.section):
print('section [' + options.section + '] is missing in ' + config_path)
return None
pic_config = dict(config.items(options.section))
if options.format:
pic_config['format'] = options.format
if options.background:
pic_config['background'] = options.background
elif 'background' in pic_config:
pass
else:
pic_config['background'] = 'transparent'
#get the translation for reading the csv
Trans = get_translation(config_path, 'translation')
for origin,translation in Trans.items():
if re.match(translation,'customerid'):
pic_config['customerid'] = origin
#chek if all files from the csv were successfully created and in the workfolder
if options.infile:
csv_path = os.path.expandvars(os.path.join('$PICCREATE_IN', CompleteEnding(options.infile, 'csv')))
if not os.path.exists(csv_path):
print('\"'+ csv_path +'\" does not exist. Please give the correct name of a csv file!')
return None
if not options.config:
print ("you must give a config file for checking the svg files")
return None
svglist = []
with open(csv_path, newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile,delimiter = ';')
for row in reader:
if row[pic_config['customerid']] not in filelist:
print ('Bild', row[pic_config['customerid']], 'fehlt')
else:
svglist.append(row[pic_config['customerid']])
filelist = svglist
#start conversion in multiple threads
if options.verbose:
print ('---- starting the conversion: ----')
startThreading(filelist,pic_config,work_path)
if options.verbose:
print ("---- conversion completed ----")
if options.dry:
print ("Dry option selected. No file was converted!")
if __name__ == '__main__':
parser = optparse.OptionParser(usage="""%prog [options]
Example usage
-------------
If no switch is given all svg files in the output folder will be converted.
Full help:
$ %prog -h (or --help for possible options)
Read Confg, create wakeup and scale this kind of jiffy box
$ %prog -c test -i test
""", version='%prog ' + __version__)
parser.add_option("-b", "--background", dest="background",
help="set the backgroundcolor. 'none' for transparent",
action='store')
parser.add_option("-c", "--config", "--cfg", dest="config",
help="config file in cfg/ with the picture sizes",
action='store')
parser.add_option("-f", "--force", dest="force", default=False,
help="create the pictures, no matter if they already exist",
action='store_true')
parser.add_option("--section", dest="section", default='png',
help="section in the config file. default is png",
action='store')
parser.add_option("--format", dest="format",
help="jpg or png, overrides the config",
action='store')
parser.add_option("-i", "--infile", dest="infile",
help="csv file with input of names/content",
action='store')
parser.add_option("-j", "--jobs", dest="jobs", default=8,
help="number of parallel jobs. default is 8",
action='store')
parser.add_option("-p", "--progress", dest="progress",
help="print a progression bar",
action='store_true')
parser.add_option("-o", "--outdir", "--svgfolder", dest="outdir",
default=os.path.expandvars('$PICCREATE_OUT'),
help="folder with the svg files, the pictures are written there too. "
"default is $PICCREATE_OUT",
action='store')
parser.add_option("-t", "--test", dest="test",
help="number of default svg-files that will be created and tested",
action='store')
parser.add_option("-v", "--verbose", dest="verbose",
help="print the status of the conversion",
action='store_true')
parser.add_option("-y", "--dry", dest="dry",
help="dont generate images, just try if the conversion would succeed",
action='store_true')
(options, args) = parser.parse_args()
if options.test:
cProfile.run('main()')
else:
main()
+19 -11
View File
@@ -1,15 +1,23 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from django.utils.encoding import smart_unicode
from django.template.defaultfilters import slugify
import configparser
import csv
import ConfigParser
import os.path
import os.path
import re
import unicodedata
def slugify(value):
''' wandelt einen String in einen ASCII Bezeichner um (wie Djangos slugify)
'''
value = unicodedata.normalize('NFKD', str(value))
value = value.encode('ascii', 'ignore').decode('ascii')
value = re.sub(r'[^\w\s-]', '', value).strip().lower()
return re.sub(r'[-\s]+', '-', value)
def csv_open(csvfile, dialect=None):
fh = open(csvfile, 'rb')
fh = open(csvfile, 'r', encoding='utf-8', newline='')
if dialect is None:
sniffer = csv.Sniffer()
dialect = sniffer.sniff(fh.read(1024), delimiters=';,\t')
@@ -24,7 +32,7 @@ class CSVRow(object):
self.firstrow = field_refinition
for i, field in enumerate(field_refinition):
try:
setattr(self, field, smart_unicode(row[i]))
setattr(self, field, row[i])
except IndexError:
setattr(self, field, None)
@@ -50,7 +58,7 @@ class CSVImport(object):
field = 'column_%d' % i
if translate:
if field in translate:
print field + ' -> '+ translate[field]
print(field + ' -> ' + translate[field])
field = translate[field]
field = slugify(field.strip()).replace('-', '_')
self.field_definition.append(field)
@@ -71,8 +79,8 @@ class CSVImport(object):
def get_translation(cfg_path, sec_name):
""" lädt die Zuordnung zwischen einem .csv Eingabefile und den
Attributen """
config = ConfigParser.RawConfigParser()
config.read(cfg_path)
config = configparser.RawConfigParser(strict=False)
config.read(cfg_path, encoding='utf-8')
trans = dict(config.items(sec_name))
return dict([[v,k] for k,v in trans.items()])
@@ -80,7 +88,7 @@ def get_translation(cfg_path, sec_name):
def CompleteEnding(path, newext):
''' Fügt die gegebene File Extension an den Filename/Pfad
'''
if not path: raise Csv2CouchError('no path given')
if not path: raise ValueError('no path given')
path, ext = os.path.splitext(path)
if not re.match(newext, ext):
path = path +'.'+newext
+267
View File
@@ -0,0 +1,267 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" erzeugt Vorschaubilder von Webseiten (Ersatz für CutyCapt + convert)
Eingabe ist eine Datei mit Zeilen der Form Kundennummer;URL
Für jede Zeile wird die Seite in einem Browser ohne Fenster geladen und der
sichtbare Bereich (Breite x Höhe aus der Sektion [webscan] der Konfiguration)
als <Kundennummer>.<format> gespeichert.
"""
__version__ = '0.1.0'
__license__ = 'BSD'
import asyncio
import configparser
import csv
import optparse
import os
import sys
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
VENV = os.path.join(PROJECT_DIR, '.venv')
INSTALL_HINTS = {
'ubuntu': '''Ubuntu / Debian:
sudo apt install python3-venv
python3 -m venv {venv}
{venv}/bin/pip install -r {project}/requirements.txt
sudo {venv}/bin/playwright install-deps chromium
{venv}/bin/playwright install chromium''',
'arch': '''Arch Linux:
python -m venv {venv}
{venv}/bin/pip install -r {project}/requirements.txt
then either the browser from Arch (recommended, Playwright does not
officially support Arch):
sudo pacman -S chromium
export PICCREATE_BROWSER=/usr/bin/chromium
or the Playwright browser:
{venv}/bin/playwright install chromium''',
'windows': r'''Windows (or simply run bin\setup-venv.bat):
py -m venv {venv}
{venv}\Scripts\pip install -r {project}\requirements.txt
{venv}\Scripts\playwright install chromium''',
}
BROWSER_HINTS = {
'ubuntu': '''Ubuntu / Debian:
sudo {venv}/bin/playwright install-deps chromium
{venv}/bin/playwright install chromium''',
'arch': '''Arch Linux:
sudo pacman -S chromium
export PICCREATE_BROWSER=/usr/bin/chromium
or the Playwright browser:
{venv}/bin/playwright install chromium''',
'windows': r'''Windows:
{venv}\Scripts\playwright install chromium''',
}
def platform_name():
''' liefert 'windows', 'ubuntu' oder 'arch' (aus /etc/os-release),
sonst None '''
if sys.platform == 'win32':
return 'windows'
try:
with open('/etc/os-release', encoding='utf-8') as fh:
info = dict(line.rstrip('\n').split('=', 1) for line in fh if '=' in line)
except OSError:
return None
ids = (info.get('ID', '') + ' ' + info.get('ID_LIKE', '')).replace('"', '').split()
if 'arch' in ids:
return 'arch'
if 'ubuntu' in ids or 'debian' in ids:
return 'ubuntu'
return None
def print_hint(hints):
''' gibt die Installationshinweise für dieses System aus
(bei unbekanntem Linux die für alle Linux-Distributionen) '''
distro = platform_name()
if distro in hints:
keys = [distro]
else:
keys = sorted(k for k in hints if k != 'windows')
for key in keys:
print(hints[key].format(venv=VENV, project=PROJECT_DIR), file=sys.stderr)
try:
from playwright.async_api import async_playwright, Error as PlaywrightError
except ImportError:
print('Playwright is not installed for', sys.executable, file=sys.stderr)
print('Install it into the project environment:', file=sys.stderr)
print_hint(INSTALL_HINTS)
sys.exit(2)
from utils import CompleteEnding
def read_config(cfg_name, section):
''' liest Breite, Höhe und Bildformat aus der Konfiguration '''
cfg_path = os.path.expandvars(os.path.join('$PICCREATE_CFG',
CompleteEnding(cfg_name, 'cfg')))
if not os.path.exists(cfg_path):
raise SystemExit('"' + cfg_path + '" does not exist. Please give the name of a config file!')
config = configparser.RawConfigParser(strict=False)
config.read(cfg_path, encoding='utf-8')
if not config.has_section(section):
raise SystemExit('section [' + section + '] is missing in ' + cfg_path)
return {
'width': config.getint(section, 'width'),
'height': config.getint(section, 'height'),
'format': config.get(section, 'format', fallback='jpg'),
}
def read_urls(infile):
''' liest die Paare (Kundennummer, URL) aus der Eingabedatei.
Eine Kopfzeile wird übersprungen, wenn ihre zweite Spalte keine URL ist.
'''
in_path = infile
if not os.path.exists(in_path):
in_path = os.path.expandvars(os.path.join('$PICCREATE_IN',
CompleteEnding(infile, 'csv')))
if not os.path.exists(in_path):
raise SystemExit('"' + in_path + '" does not exist. Please give the name to a input file!')
jobs = []
with open(in_path, encoding='utf-8', newline='') as fh:
for n, row in enumerate(csv.reader(fh, delimiter=';')):
row = [field.strip() for field in row]
if not row or not row[0] or row[0].startswith('#'):
continue
url = row[-1] if len(row) > 1 else ''
if n == 0 and '.' not in url:
continue
if url and '://' not in url:
url = 'http://' + url
jobs.append((row[0], url))
return jobs
async def capture(browser, customer_id, url, out_path, pic_config, timeout):
''' lädt eine Seite und speichert den sichtbaren Bereich als Bild '''
page = await browser.new_page(viewport={'width': pic_config['width'],
'height': pic_config['height']})
try:
await page.goto(url, timeout=timeout, wait_until='load')
kwargs = {'path': out_path}
if pic_config['format'] in ('jpg', 'jpeg'):
kwargs['type'] = 'jpeg'
kwargs['quality'] = 90
else:
kwargs['type'] = 'png'
await page.screenshot(**kwargs)
finally:
await page.close()
async def run(jobs, pic_config, options):
out_dir = options.outdir
timeout = int(options.timeout) * 1000
semaphore = asyncio.Semaphore(int(options.jobs))
failed = []
async with async_playwright() as p:
launch_args = {}
if options.browser:
if not os.path.isfile(options.browser):
print('browser', options.browser, 'does not exist.', file=sys.stderr)
print_hint(BROWSER_HINTS)
return None
launch_args['executable_path'] = options.browser
try:
browser = await p.chromium.launch(**launch_args)
except PlaywrightError as e:
print('could not start the browser:', str(e).splitlines()[0], file=sys.stderr)
print_hint(BROWSER_HINTS)
return None
async def worker(customer_id, url):
out_path = os.path.join(out_dir, customer_id + '.' + pic_config['format'])
if not url:
print(customer_id, 'has no url - skipped', file=sys.stderr)
failed.append(customer_id)
return
if os.path.exists(out_path) and not options.force:
if options.verbose:
print(out_path, 'exists')
return
async with semaphore:
if options.verbose:
print('loading', customer_id, url)
try:
await capture(browser, customer_id, url, out_path, pic_config, timeout)
print(out_path)
except PlaywrightError as e:
first_line = str(e).splitlines()[0]
print(customer_id, url, 'failed:', first_line, file=sys.stderr)
failed.append(customer_id)
await asyncio.gather(*(worker(cid, url) for cid, url in jobs))
await browser.close()
return failed
def main():
if not os.path.isdir(options.outdir):
print('"' + options.outdir + '" does not exist. Please give the path to the directory for the pictures!')
return 1
pic_config = read_config(options.config, options.section)
if options.format:
pic_config['format'] = options.format
jobs = read_urls(options.infile)
if not jobs:
print('no urls found in', options.infile)
return 1
failed = asyncio.run(run(jobs, pic_config, options))
if failed is None:
return 2
if failed:
print(len(failed), 'of', len(jobs), 'pages failed:', ', '.join(failed), file=sys.stderr)
return 1
return 0
if __name__ == '__main__':
parser = optparse.OptionParser(usage="""%prog [options]
Example usage
-------------
Screenshots of all urls in in/kunden.csv (lines: customerid;url):
$ %prog -c webscan -i kunden
""", version='%prog ' + __version__)
parser.add_option("-c", "--config", "--cfg", dest="config", default="webscan",
help="config file with a [webscan] section (width, height, format)",
metavar="CONFIG_FILE")
parser.add_option("--section", dest="section", default="webscan",
help="section in the config file. default is webscan")
parser.add_option("-i", "--infile", dest="infile",
help="file with lines customerid;url")
parser.add_option("-o", "--outdir", dest="outdir", default=os.path.expandvars('$PICCREATE_OUT'),
help="directory where the pictures will be saved")
parser.add_option("--format", dest="format",
help="jpg or png, overrides the config")
parser.add_option("-j", "--jobs", dest="jobs", default=4,
help="number of pages loaded in parallel. default is 4")
parser.add_option("--timeout", dest="timeout", default=20,
help="seconds to wait for a page. default is 20")
parser.add_option("-f", "--force", dest="force", action='store_true',
help="create the pictures, no matter if they already exist")
parser.add_option("--browser", dest="browser",
default=os.environ.get('PICCREATE_BROWSER'),
help="path to a chromium executable instead of the playwright one "
"(default: $PICCREATE_BROWSER)")
parser.add_option("-v", "--verbose", dest="verbose", action='store_true',
help="print more information")
(options, args) = parser.parse_args()
if not options.infile:
parser.error('please give an input file with -i')
sys.exit(main())
# vim: set tw=80 ft=python ts=4 sw=4:
+1
View File
@@ -0,0 +1 @@
playwright