Compare commits
10
Commits
dc8e5230e2
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5598f2304 | ||
|
|
6bdaa62a7d | ||
|
|
28f2bcdb32 | ||
|
|
29c066b45a | ||
|
|
4897883938 | ||
|
|
c884f4fd92 | ||
|
|
7bd43e9e00 | ||
|
|
31c8d97817 | ||
|
|
75db9b7024 | ||
|
|
d4cb697f14 |
@@ -0,0 +1,3 @@
|
||||
*.bat text eol=crlf
|
||||
*.sh text eol=lf
|
||||
*.py text eol=lf
|
||||
@@ -0,0 +1,3 @@
|
||||
.venv/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
@@ -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
@@ -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}"
|
||||
|
||||
@@ -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
@@ -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 "$@"
|
||||
|
||||
@@ -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
@@ -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}"
|
||||
|
||||
@@ -1,151 +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 check if the jpg already exist
|
||||
-b set the background to transparent
|
||||
-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`
|
||||
}
|
||||
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=''
|
||||
FILE_FORMAT='png'
|
||||
while getopts "j:i:kc:s:o:hc:f: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 ;;
|
||||
f) FILE_FORMAT=$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.$FILE_FORMAT
|
||||
if [ -s $FILE_PATH ] && [ $CHECKFILE -eq 1 ]
|
||||
then
|
||||
echo $PIC_NAME exists
|
||||
else
|
||||
RESIZE="${width}x${height}"
|
||||
SWITCHES="-density $density"'x'"$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
@@ -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 "$@"
|
||||
|
||||
@@ -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%
|
||||
@@ -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
@@ -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
|
||||
# ------------------
|
||||
|
||||
|
||||
@@ -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
|
||||
Executable
+47
@@ -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
|
||||
@@ -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
@@ -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 "$@"
|
||||
|
||||
-191
@@ -1,191 +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 file where the web-adresses are
|
||||
-k chek if the output file 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`
|
||||
}
|
||||
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
|
||||
echo $INPUT_FILENAME
|
||||
cfg_parser $CFG_PATH "$FILE_FORMAT"
|
||||
echo 'using width '$width
|
||||
echo 'using height '$height
|
||||
if [ -z "$width" ]; then
|
||||
echo "please give width=xxx in section [$FILE_FORMAT] in config $CFG_FILENAME."
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$height" ]; then
|
||||
echo "please give height=xxx in section [$FILE_FORMAT] in config $CFG_FILENAME."
|
||||
exit 1
|
||||
fi
|
||||
FORMAT="$format"
|
||||
}
|
||||
|
||||
|
||||
# parse command line
|
||||
if [ $# -eq 0 ]; then # must be at least one arg
|
||||
echo "$USAGE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CHECKFILE=1
|
||||
SLEEPTIME=0.5
|
||||
BACKGROUND=''
|
||||
FILE_FORMAT='webscan'
|
||||
OUTDIR=$PICCREATE_OUT
|
||||
while getopts "j:i:kc:s:o:hc:f" OPT; do # "j:" waits for an argument "h" doesnt
|
||||
case $OPT in
|
||||
h) echo "$USAGE"
|
||||
exit 0 ;;
|
||||
j) MAX_NPROC=$OPTARG ;;
|
||||
k) CHECKFILE=0 ;;
|
||||
c) CFG_FILENAME=$OPTARG ;;
|
||||
o) OUTDIR=$OPTARG ;;
|
||||
s) SLEEPTIME=$OPTARG ;;
|
||||
i) INPUT_FILENAME=$OPTARG ;;
|
||||
f) FORMAT=$OPTARG ;;
|
||||
*) echo "$USAGE" >&2; exit 1; # Default.
|
||||
esac
|
||||
done
|
||||
|
||||
# Main program
|
||||
echo Using $MAX_NPROC parallel threads
|
||||
check_params
|
||||
|
||||
# Starte die Abspeichern der ganzen Webseite als ganzes
|
||||
echo $INPUT_FILE
|
||||
for i in `cat $INPUT_FILENAME`
|
||||
do
|
||||
CUSTOMER_ID=`echo "$i" | sed -e 's/;.*//g'`
|
||||
CUSTOMER_URL=`echo "$i" |sed -e 's/.*;//g'`
|
||||
|
||||
FILE_PATH=$OUTDIR/$CUSTOMER_ID.$FORMAT
|
||||
echo $FILE_PATH
|
||||
wait
|
||||
if [ -z $CUSTOMER_URL ]
|
||||
then
|
||||
echo 'i need a url!!'
|
||||
echo $CUSTOMER_ID 'did not have an url, ist was set to www.syntops.de instead'>> logfile.txt
|
||||
CUSTOMER_URL='www.syntops.de'
|
||||
sleep
|
||||
fi
|
||||
if [ -s $FILE_PATH ] && [ $CHECKFILE -eq 1 ]
|
||||
then
|
||||
echo $FILE_PATH exists
|
||||
else
|
||||
CMDLINE="$CUTYCAPT/./CutyCapt --url=http://$CUSTOMER_URL --max-wait=20000 --min-width=800 --min-height=600 --out=$FILE_PATH"
|
||||
$CMDLINE
|
||||
wait
|
||||
fi
|
||||
done
|
||||
|
||||
wait # wait for all processes to finish before exit
|
||||
echo "...all Webpages have been saved!"
|
||||
echo ""
|
||||
echo "Converting the pictures..."
|
||||
echo " "
|
||||
|
||||
|
||||
# Zuschnitt der seite auf das benötigte Format
|
||||
# Das Bild wir hierfür erst gleichmäßig auf die forgegebene Breite Skaliert
|
||||
# und anschließend die unten herüberstehende Höhe abgeschnitten
|
||||
echo $outdir
|
||||
for i in `ls $OUTDIR| grep .$FORMAT `
|
||||
do
|
||||
FILE_PATH_IN=$OUTDIR/$i
|
||||
FILE_PATH_OUT=$OUTDIR/'beschnitten'/$i
|
||||
|
||||
if [ -s $FILE_PATH_OUT ] && [ $CHECKFILE -eq 1 ]
|
||||
then
|
||||
echo $PIC_NAME exists
|
||||
else
|
||||
RESIZE="${width}"
|
||||
SLICE="${width}x${height}"
|
||||
CMDLINE="convert $FILE_PATH_IN -resize $RESIZE -crop $SLICE $FILE_PATH_OUT"
|
||||
echo $CMDLINE
|
||||
(echo $ID; $CMDLINE) &
|
||||
|
||||
|
||||
while [ `ps clx|grep -w 'convert' |wc -l` -ge $MAX_NPROC ]; do
|
||||
sleep $SLEEPTIME
|
||||
done
|
||||
fi
|
||||
# auskommentiert da bereits runtergeladene seiten behalten werden sollen
|
||||
#rm $FILE_PATH
|
||||
|
||||
done
|
||||
|
||||
wait # wait for all processes to finish before exit
|
||||
|
||||
|
||||
rm $OUTDIR/'beschnitten'/*-[1-9].$FORMAT
|
||||
for i in `ls $OUTDIR/'beschnitten'/`
|
||||
do
|
||||
first=`basename $i -0.jpg`
|
||||
second=`basename $first .jpg`
|
||||
mv $OUTDIR/'beschnitten'/$i $OUTDIR/'beschnitten'/$second.jpg
|
||||
|
||||
done
|
||||
|
||||
|
||||
# vi:set wrapmargin=0 ai showmatch ts=4 syntax=sh:
|
||||
@@ -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%
|
||||
+8
-3
@@ -1,4 +1,9 @@
|
||||
#!/bin/bash
|
||||
. setenv.sh
|
||||
export CUTYCAPT=$PICCREATE_LIBS/CutyCapt
|
||||
$PICCREATE_BIN/webscan "$@"
|
||||
. "$(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 "$@"
|
||||
|
||||
+328
@@ -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`):
|
||||
|
||||

|
||||
|
||||
Lösung (`cfg/punkteloesung.cfg`, Font `ausgangsschrift_verbunden`, ohne Hintergrund):
|
||||
|
||||

|
||||
|
||||
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 |
Binary file not shown.
@@ -1,662 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CutyCapt - A Qt WebKit Web Page Rendering Capture Utility
|
||||
//
|
||||
// Copyright (C) 2003-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||
//
|
||||
// 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 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 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.
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QApplication>
|
||||
#include <QtWebKit>
|
||||
#include <QtGui>
|
||||
#include <QSvgGenerator>
|
||||
#include <QPrinter>
|
||||
#include <QTimer>
|
||||
#include <QByteArray>
|
||||
#include <QNetworkRequest>
|
||||
#include "CutyCapt.hpp"
|
||||
|
||||
#if QT_VERSION >= 0x040600 && 0
|
||||
#define CUTYCAPT_SCRIPT 1
|
||||
#endif
|
||||
|
||||
#ifdef STATIC_PLUGINS
|
||||
Q_IMPORT_PLUGIN(qjpeg)
|
||||
Q_IMPORT_PLUGIN(qgif)
|
||||
Q_IMPORT_PLUGIN(qtiff)
|
||||
Q_IMPORT_PLUGIN(qsvg)
|
||||
Q_IMPORT_PLUGIN(qmng)
|
||||
Q_IMPORT_PLUGIN(qico)
|
||||
#endif
|
||||
|
||||
static struct _CutyExtMap {
|
||||
CutyCapt::OutputFormat id;
|
||||
const char* extension;
|
||||
const char* identifier;
|
||||
} const CutyExtMap[] = {
|
||||
{ CutyCapt::SvgFormat, ".svg", "svg" },
|
||||
{ CutyCapt::PdfFormat, ".pdf", "pdf" },
|
||||
{ CutyCapt::PsFormat, ".ps", "ps" },
|
||||
{ CutyCapt::InnerTextFormat, ".txt", "itext" },
|
||||
{ CutyCapt::HtmlFormat, ".html", "html" },
|
||||
{ CutyCapt::RenderTreeFormat, ".rtree", "rtree" },
|
||||
{ CutyCapt::JpegFormat, ".jpeg", "jpeg" },
|
||||
{ CutyCapt::PngFormat, ".png", "png" },
|
||||
{ CutyCapt::MngFormat, ".mng", "mng" },
|
||||
{ CutyCapt::TiffFormat, ".tiff", "tiff" },
|
||||
{ CutyCapt::GifFormat, ".gif", "gif" },
|
||||
{ CutyCapt::BmpFormat, ".bmp", "bmp" },
|
||||
{ CutyCapt::PpmFormat, ".ppm", "ppm" },
|
||||
{ CutyCapt::XbmFormat, ".xbm", "xbm" },
|
||||
{ CutyCapt::XpmFormat, ".xpm", "xpm" },
|
||||
{ CutyCapt::OtherFormat, "", "" }
|
||||
};
|
||||
|
||||
QString
|
||||
CutyPage::chooseFile(QWebFrame* /*frame*/, const QString& /*suggestedFile*/) {
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
bool
|
||||
CutyPage::javaScriptConfirm(QWebFrame* /*frame*/, const QString& /*msg*/) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CutyPage::javaScriptPrompt(QWebFrame* /*frame*/,
|
||||
const QString& /*msg*/,
|
||||
const QString& /*defaultValue*/,
|
||||
QString* /*result*/) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CutyPage::javaScriptConsoleMessage(const QString& /*message*/,
|
||||
int /*lineNumber*/,
|
||||
const QString& /*sourceID*/) {
|
||||
// noop
|
||||
}
|
||||
|
||||
void
|
||||
CutyPage::javaScriptAlert(QWebFrame* /*frame*/, const QString& msg) {
|
||||
|
||||
if (mPrintAlerts)
|
||||
qDebug() << "[alert]" << msg;
|
||||
|
||||
if (mAlertString == msg) {
|
||||
QTimer::singleShot(10, mCutyCapt, SLOT(Delayed()));
|
||||
}
|
||||
}
|
||||
|
||||
QString
|
||||
CutyPage::userAgentForUrl(const QUrl& url) const {
|
||||
|
||||
if (!mUserAgent.isNull())
|
||||
return mUserAgent;
|
||||
|
||||
return QWebPage::userAgentForUrl(url);
|
||||
}
|
||||
|
||||
void
|
||||
CutyPage::setUserAgent(const QString& userAgent) {
|
||||
mUserAgent = userAgent;
|
||||
}
|
||||
|
||||
void
|
||||
CutyPage::setAlertString(const QString& alertString) {
|
||||
mAlertString = alertString;
|
||||
}
|
||||
|
||||
QString
|
||||
CutyPage::getAlertString() {
|
||||
return mAlertString;
|
||||
}
|
||||
|
||||
void
|
||||
CutyPage::setCutyCapt(CutyCapt* cutyCapt) {
|
||||
mCutyCapt = cutyCapt;
|
||||
}
|
||||
|
||||
void
|
||||
CutyPage::setPrintAlerts(bool printAlerts) {
|
||||
mPrintAlerts = printAlerts;
|
||||
}
|
||||
|
||||
void
|
||||
CutyPage::setAttribute(QWebSettings::WebAttribute option,
|
||||
const QString& value) {
|
||||
|
||||
if (value == "on")
|
||||
settings()->setAttribute(option, true);
|
||||
else if (value == "off")
|
||||
settings()->setAttribute(option, false);
|
||||
else
|
||||
(void)0; // TODO: ...
|
||||
}
|
||||
|
||||
// TODO: Consider merging some of main() and CutyCap
|
||||
|
||||
CutyCapt::CutyCapt(CutyPage* page, const QString& output, int delay, OutputFormat format,
|
||||
const QString& scriptProp, const QString& scriptCode) {
|
||||
mPage = page;
|
||||
mOutput = output;
|
||||
mDelay = delay;
|
||||
mSawInitialLayout = false;
|
||||
mSawDocumentComplete = false;
|
||||
mFormat = format;
|
||||
mScriptProp = scriptProp;
|
||||
mScriptCode = scriptCode;
|
||||
mScriptObj = new QObject();
|
||||
|
||||
// This is not really nice, but some restructuring work is
|
||||
// needed anyway, so this should not be that bad for now.
|
||||
mPage->setCutyCapt(this);
|
||||
}
|
||||
|
||||
void
|
||||
CutyCapt::InitialLayoutCompleted() {
|
||||
mSawInitialLayout = true;
|
||||
|
||||
if (mSawInitialLayout && mSawDocumentComplete)
|
||||
TryDelayedRender();
|
||||
}
|
||||
|
||||
void
|
||||
CutyCapt::DocumentComplete(bool /*ok*/) {
|
||||
mSawDocumentComplete = true;
|
||||
|
||||
if (mSawInitialLayout && mSawDocumentComplete)
|
||||
TryDelayedRender();
|
||||
}
|
||||
|
||||
void
|
||||
CutyCapt::JavaScriptWindowObjectCleared() {
|
||||
|
||||
if (!mScriptProp.isEmpty()) {
|
||||
QVariant var = mPage->mainFrame()->evaluateJavaScript(mScriptProp);
|
||||
QObject* obj = var.value<QObject*>();
|
||||
|
||||
if (obj == mScriptObj)
|
||||
return;
|
||||
|
||||
mPage->mainFrame()->addToJavaScriptWindowObject(mScriptProp, mScriptObj);
|
||||
}
|
||||
|
||||
mPage->mainFrame()->evaluateJavaScript(mScriptCode);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
CutyCapt::TryDelayedRender() {
|
||||
|
||||
if (!mPage->getAlertString().isEmpty())
|
||||
return;
|
||||
|
||||
if (mDelay > 0) {
|
||||
QTimer::singleShot(mDelay, this, SLOT(Delayed()));
|
||||
return;
|
||||
}
|
||||
|
||||
saveSnapshot();
|
||||
QApplication::exit();
|
||||
}
|
||||
|
||||
void
|
||||
CutyCapt::Timeout() {
|
||||
saveSnapshot();
|
||||
QApplication::exit();
|
||||
}
|
||||
|
||||
void
|
||||
CutyCapt::Delayed() {
|
||||
saveSnapshot();
|
||||
QApplication::exit();
|
||||
}
|
||||
|
||||
void
|
||||
CutyCapt::saveSnapshot() {
|
||||
QWebFrame *mainFrame = mPage->mainFrame();
|
||||
QPainter painter;
|
||||
const char* format = NULL;
|
||||
|
||||
for (int ix = 0; CutyExtMap[ix].id != OtherFormat; ++ix)
|
||||
if (CutyExtMap[ix].id == mFormat)
|
||||
format = CutyExtMap[ix].identifier; //, break;
|
||||
|
||||
// TODO: sometimes contents/viewport can have size 0x0
|
||||
// in which case saving them will fail. This is likely
|
||||
// the result of the method being called too early. So
|
||||
// far I've been unable to find a workaround, except
|
||||
// using --delay with some substantial wait time. I've
|
||||
// tried to resize multiple time, make a fake render,
|
||||
// check for other events... This is primarily a problem
|
||||
// under my Ubuntu virtual machine.
|
||||
|
||||
mPage->setViewportSize( mainFrame->contentsSize() );
|
||||
|
||||
switch (mFormat) {
|
||||
case SvgFormat: {
|
||||
QSvgGenerator svg;
|
||||
svg.setFileName(mOutput);
|
||||
svg.setSize(mPage->viewportSize());
|
||||
painter.begin(&svg);
|
||||
mainFrame->render(&painter);
|
||||
painter.end();
|
||||
break;
|
||||
}
|
||||
case PdfFormat:
|
||||
case PsFormat: {
|
||||
QPrinter printer;
|
||||
printer.setPageSize(QPrinter::A4);
|
||||
printer.setOutputFileName(mOutput);
|
||||
// TODO: change quality here?
|
||||
mainFrame->print(&printer);
|
||||
break;
|
||||
}
|
||||
case RenderTreeFormat:
|
||||
case InnerTextFormat:
|
||||
case HtmlFormat: {
|
||||
QFile file(mOutput);
|
||||
file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
QTextStream s(&file);
|
||||
s.setCodec("utf-8");
|
||||
s << (mFormat == RenderTreeFormat ? mainFrame->renderTreeDump() :
|
||||
mFormat == InnerTextFormat ? mainFrame->toPlainText() :
|
||||
mFormat == HtmlFormat ? mainFrame->toHtml() :
|
||||
"bug");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
QImage image(mPage->viewportSize(), QImage::Format_ARGB32);
|
||||
painter.begin(&image);
|
||||
mainFrame->render(&painter);
|
||||
painter.end();
|
||||
// TODO: add quality
|
||||
image.save(mOutput, format);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
CaptHelp(void) {
|
||||
printf("%s",
|
||||
" -----------------------------------------------------------------------------\n"
|
||||
" Usage: CutyCapt --url=http://www.example.org/ --out=localfile.png \n"
|
||||
" -----------------------------------------------------------------------------\n"
|
||||
" --help Print this help page and exit \n"
|
||||
" --url=<url> The URL to capture (http:...|file:...|...) \n"
|
||||
" --out=<path> The target file (.png|pdf|ps|svg|jpeg|...) \n"
|
||||
" --out-format=<f> Like extension in --out, overrides heuristic \n"
|
||||
// " --out-quality=<int> Output format quality from 1 to 100 \n"
|
||||
" --min-width=<int> Minimal width for the image (default: 800) \n"
|
||||
" --min-height=<int> Minimal height for the image (default: 600) \n"
|
||||
" --max-wait=<ms> Don't wait more than (default: 90000, inf: 0)\n"
|
||||
" --delay=<ms> After successful load, wait (default: 0) \n"
|
||||
// " --user-styles=<url> Location of user style sheet (deprecated) \n"
|
||||
" --user-style-path=<path> Location of user style sheet file, if any \n"
|
||||
" --user-style-string=<css> User style rules specified as text \n"
|
||||
" --header=<name>:<value> request header; repeatable; some can't be set\n"
|
||||
" --method=<get|post|put> Specifies the request method (default: get) \n"
|
||||
" --body-string=<string> Unencoded request body (default: none) \n"
|
||||
" --body-base64=<base64> Base64-encoded request body (default: none) \n"
|
||||
" --app-name=<name> appName used in User-Agent; default is none \n"
|
||||
" --app-version=<version> appVers used in User-Agent; default is none \n"
|
||||
" --user-agent=<string> Override the User-Agent header Qt would set \n"
|
||||
" --javascript=<on|off> JavaScript execution (default: on) \n"
|
||||
" --java=<on|off> Java execution (default: unknown) \n"
|
||||
" --plugins=<on|off> Plugin execution (default: unknown) \n"
|
||||
" --private-browsing=<on|off> Private browsing (default: unknown) \n"
|
||||
" --auto-load-images=<on|off> Automatic image loading (default: on) \n"
|
||||
" --js-can-open-windows=<on|off> Script can open windows? (default: unknown) \n"
|
||||
" --js-can-access-clipboard=<on|off> Script clipboard privs (default: unknown)\n"
|
||||
#if QT_VERSION >= 0x040500
|
||||
" --print-backgrounds=<on|off> Backgrounds in PDF/PS output (default: off) \n"
|
||||
" --zoom-factor=<float> Page zoom factor (default: no zooming) \n"
|
||||
" --zoom-text-only=<on|off> Whether to zoom only the text (default: off) \n"
|
||||
" --http-proxy=<url> Address for HTTP proxy server (default: none)\n"
|
||||
#endif
|
||||
#if CUTYCAPT_SCRIPT
|
||||
" --inject-script=<path> JavaScript that will be injected into pages \n"
|
||||
" --script-object=<string> Property to hold state for injected script \n"
|
||||
" --expect-alert=<string> Try waiting for alert(string) before capture \n"
|
||||
" --debug-print-alerts Prints out alert(...) strings for debugging. \n"
|
||||
#endif
|
||||
" -----------------------------------------------------------------------------\n"
|
||||
" <f> is svg,ps,pdf,itext,html,rtree,png,jpeg,mng,tiff,gif,bmp,ppm,xbm,xpm \n"
|
||||
" -----------------------------------------------------------------------------\n"
|
||||
#if CUTYCAPT_SCRIPT
|
||||
" The `inject-script` option can be used to inject script code into loaded web \n"
|
||||
" pages. The code is called whenever the `javaScriptWindowObjectCleared` signal\n"
|
||||
" is received. When `script-object` is set, an object under the specified name \n"
|
||||
" will be available to the script to maintain state across page loads. When the\n"
|
||||
" `expect-alert` option is specified, the shot will be taken when a script in- \n"
|
||||
" vokes alert(string) with the string if that happens before `max-wait`. These \n"
|
||||
" options effectively allow you to remote control the browser and the web page.\n"
|
||||
" This an experimental and easily abused and misused feature. Use with caution.\n"
|
||||
" -----------------------------------------------------------------------------\n"
|
||||
#endif
|
||||
" http://cutycapt.sf.net - (c) 2003-2010 Bjoern Hoehrmann - bjoern@hoehrmann.de\n"
|
||||
"");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
|
||||
int argHelp = 0;
|
||||
int argDelay = 0;
|
||||
int argSilent = 0;
|
||||
int argMinWidth = 800;
|
||||
int argMinHeight = 600;
|
||||
int argMaxWait = 90000;
|
||||
int argVerbosity = 0;
|
||||
|
||||
const char* argUrl = NULL;
|
||||
const char* argUserStyle = NULL;
|
||||
const char* argUserStylePath = NULL;
|
||||
const char* argUserStyleString = NULL;
|
||||
const char* argIconDbPath = NULL;
|
||||
const char* argInjectScript = NULL;
|
||||
const char* argScriptObject = NULL;
|
||||
QString argOut;
|
||||
|
||||
CutyCapt::OutputFormat format = CutyCapt::OtherFormat;
|
||||
|
||||
QApplication app(argc, argv, true);
|
||||
CutyPage page;
|
||||
|
||||
QNetworkAccessManager::Operation method =
|
||||
QNetworkAccessManager::GetOperation;
|
||||
QByteArray body;
|
||||
QNetworkRequest req;
|
||||
QNetworkAccessManager manager;
|
||||
|
||||
// Parse command line parameters
|
||||
for (int ax = 1; ax < argc; ++ax) {
|
||||
size_t nlen;
|
||||
|
||||
const char* s = argv[ax];
|
||||
const char* value;
|
||||
|
||||
// boolean options
|
||||
if (strcmp("--silent", s) == 0) {
|
||||
argSilent = 1;
|
||||
continue;
|
||||
|
||||
} else if (strcmp("--help", s) == 0) {
|
||||
argHelp = 1;
|
||||
break;
|
||||
|
||||
} else if (strcmp("--verbose", s) == 0) {
|
||||
argVerbosity++;
|
||||
continue;
|
||||
|
||||
#if CUTYCAPT_SCRIPT
|
||||
} else if (strcmp("--debug-print-alerts", s) == 0) {
|
||||
page.setPrintAlerts(true);
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
|
||||
value = strchr(s, '=');
|
||||
|
||||
if (value == NULL) {
|
||||
// TODO: error
|
||||
argHelp = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
nlen = value++ - s;
|
||||
|
||||
// --name=value options
|
||||
if (strncmp("--url", s, nlen) == 0) {
|
||||
argUrl = value;
|
||||
|
||||
} else if (strncmp("--min-width", s, nlen) == 0) {
|
||||
// TODO: add error checking here?
|
||||
argMinWidth = (unsigned int)atoi(value);
|
||||
|
||||
} else if (strncmp("--min-height", s, nlen) == 0) {
|
||||
// TODO: add error checking here?
|
||||
argMinHeight = (unsigned int)atoi(value);
|
||||
|
||||
} else if (strncmp("--delay", s, nlen) == 0) {
|
||||
// TODO: see above
|
||||
argDelay = (unsigned int)atoi(value);
|
||||
|
||||
} else if (strncmp("--max-wait", s, nlen) == 0) {
|
||||
// TODO: see above
|
||||
argMaxWait = (unsigned int)atoi(value);
|
||||
|
||||
} else if (strncmp("--out", s, nlen) == 0) {
|
||||
argOut = value;
|
||||
|
||||
if (format == CutyCapt::OtherFormat)
|
||||
for (int ix = 0; CutyExtMap[ix].id != CutyCapt::OtherFormat; ++ix)
|
||||
if (argOut.endsWith(CutyExtMap[ix].extension))
|
||||
format = CutyExtMap[ix].id; //, break;
|
||||
|
||||
} else if (strncmp("--user-styles", s, nlen) == 0) {
|
||||
// This option is provided for backwards-compatibility only
|
||||
argUserStyle = value;
|
||||
|
||||
} else if (strncmp("--user-style-path", s, nlen) == 0) {
|
||||
argUserStylePath = value;
|
||||
|
||||
} else if (strncmp("--user-style-string", s, nlen) == 0) {
|
||||
argUserStyleString = value;
|
||||
|
||||
} else if (strncmp("--icon-database-path", s, nlen) == 0) {
|
||||
argIconDbPath = value;
|
||||
|
||||
} else if (strncmp("--auto-load-images", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::AutoLoadImages, value);
|
||||
|
||||
} else if (strncmp("--javascript", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::JavascriptEnabled, value);
|
||||
|
||||
} else if (strncmp("--java", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::JavaEnabled, value);
|
||||
|
||||
} else if (strncmp("--plugins", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::PluginsEnabled, value);
|
||||
|
||||
} else if (strncmp("--private-browsing", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::PrivateBrowsingEnabled, value);
|
||||
|
||||
} else if (strncmp("--js-can-open-windows", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::JavascriptCanOpenWindows, value);
|
||||
|
||||
} else if (strncmp("--js-can-access-clipboard", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::JavascriptCanAccessClipboard, value);
|
||||
|
||||
} else if (strncmp("--developer-extras", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::DeveloperExtrasEnabled, value);
|
||||
|
||||
} else if (strncmp("--links-included-in-focus-chain", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::LinksIncludedInFocusChain, value);
|
||||
|
||||
#if QT_VERSION >= 0x040500
|
||||
} else if (strncmp("--print-backgrounds", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::PrintElementBackgrounds, value);
|
||||
|
||||
} else if (strncmp("--zoom-factor", s, nlen) == 0) {
|
||||
page.mainFrame()->setZoomFactor(QString(value).toFloat());
|
||||
|
||||
} else if (strncmp("--zoom-text-only", s, nlen) == 0) {
|
||||
page.setAttribute(QWebSettings::ZoomTextOnly, value);
|
||||
|
||||
} else if (strncmp("--http-proxy", s, nlen) == 0) {
|
||||
QUrl p = QUrl::fromEncoded(value);
|
||||
QNetworkProxy proxy = QNetworkProxy(QNetworkProxy::HttpProxy,
|
||||
p.host(), p.port(80), p.userName(), p.password());
|
||||
manager.setProxy(proxy);
|
||||
page.setNetworkAccessManager(&manager);
|
||||
#endif
|
||||
|
||||
#if CUTYCAPT_SCRIPT
|
||||
} else if (strncmp("--inject-script", s, nlen) == 0) {
|
||||
argInjectScript = value;
|
||||
|
||||
} else if (strncmp("--script-object", s, nlen) == 0) {
|
||||
argScriptObject = value;
|
||||
|
||||
} else if (strncmp("--expect-alert", s, nlen) == 0) {
|
||||
page.setAlertString(value);
|
||||
#endif
|
||||
|
||||
} else if (strncmp("--app-name", s, nlen) == 0) {
|
||||
app.setApplicationName(value);
|
||||
|
||||
} else if (strncmp("--app-version", s, nlen) == 0) {
|
||||
app.setApplicationVersion(value);
|
||||
|
||||
} else if (strncmp("--body-base64", s, nlen) == 0) {
|
||||
body = QByteArray::fromBase64(value);
|
||||
|
||||
} else if (strncmp("--body-string", s, nlen) == 0) {
|
||||
body = QByteArray(value);
|
||||
|
||||
} else if (strncmp("--user-agent", s, nlen) == 0) {
|
||||
page.setUserAgent(value);
|
||||
|
||||
} else if (strncmp("--out-format", s, nlen) == 0) {
|
||||
for (int ix = 0; CutyExtMap[ix].id != CutyCapt::OtherFormat; ++ix)
|
||||
if (strcmp(value, CutyExtMap[ix].identifier) == 0)
|
||||
format = CutyExtMap[ix].id; //, break;
|
||||
|
||||
if (format == CutyCapt::OtherFormat) {
|
||||
// TODO: error
|
||||
argHelp = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (strncmp("--header", s, nlen) == 0) {
|
||||
const char* hv = strchr(value, ':');
|
||||
|
||||
if (hv == NULL) {
|
||||
// TODO: error
|
||||
argHelp = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
req.setRawHeader(QByteArray(value, hv - value), hv + 1);
|
||||
|
||||
} else if (strncmp("--method", s, nlen) == 0) {
|
||||
if (strcmp("value", "get") == 0)
|
||||
method = QNetworkAccessManager::GetOperation;
|
||||
else if (strcmp("value", "put") == 0)
|
||||
method = QNetworkAccessManager::PutOperation;
|
||||
else if (strcmp("value", "post") == 0)
|
||||
method = QNetworkAccessManager::PostOperation;
|
||||
else if (strcmp("value", "head") == 0)
|
||||
method = QNetworkAccessManager::HeadOperation;
|
||||
else
|
||||
(void)0; // TODO: ...
|
||||
|
||||
} else {
|
||||
// TODO: error
|
||||
argHelp = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (argUrl == NULL || argOut == NULL || argHelp) {
|
||||
CaptHelp();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// This used to use QUrl(argUrl) but that escapes %hh sequences
|
||||
// even though it should not, as URLs can assumed to be escaped.
|
||||
req.setUrl( QUrl::fromEncoded(argUrl) );
|
||||
|
||||
QString scriptProp(argScriptObject);
|
||||
QString scriptCode;
|
||||
|
||||
if (argInjectScript) {
|
||||
QFile file(argInjectScript);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
stream.setAutoDetectUnicode(true);
|
||||
scriptCode = stream.readAll();
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
CutyCapt main(&page, argOut, argDelay, format, scriptProp, scriptCode);
|
||||
|
||||
app.connect(&page,
|
||||
SIGNAL(loadFinished(bool)),
|
||||
&main,
|
||||
SLOT(DocumentComplete(bool)));
|
||||
|
||||
app.connect(page.mainFrame(),
|
||||
SIGNAL(initialLayoutCompleted()),
|
||||
&main,
|
||||
SLOT(InitialLayoutCompleted()));
|
||||
|
||||
if (argMaxWait > 0) {
|
||||
// TODO: Should this also register one for the application?
|
||||
QTimer::singleShot(argMaxWait, &main, SLOT(Timeout()));
|
||||
}
|
||||
|
||||
if (argUserStyle != NULL)
|
||||
// TODO: does this need any syntax checking?
|
||||
page.settings()->setUserStyleSheetUrl( QUrl::fromEncoded(argUserStyle) );
|
||||
|
||||
if (argUserStylePath != NULL) {
|
||||
page.settings()->setUserStyleSheetUrl( QUrl::fromLocalFile(argUserStylePath) );
|
||||
}
|
||||
|
||||
if (argUserStyleString != NULL) {
|
||||
QUrl data("data:text/css;charset=utf-8;base64," +
|
||||
QByteArray(argUserStyleString).toBase64());
|
||||
page.settings()->setUserStyleSheetUrl( data );
|
||||
}
|
||||
|
||||
if (argIconDbPath != NULL)
|
||||
// TODO: does this need any syntax checking?
|
||||
page.settings()->setIconDatabasePath(argUserStyle);
|
||||
|
||||
// The documentation does not say, but it seems the mainFrame
|
||||
// will never change, so we can set this here. Otherwise we'd
|
||||
// have to set this in snapshot and trigger an update, which
|
||||
// is not currently possible (Qt 4.4.0) as far as I can tell.
|
||||
page.mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
|
||||
page.mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
|
||||
page.setViewportSize( QSize(argMinWidth, argMinHeight) );
|
||||
|
||||
#if CUTYCAPT_SCRIPT
|
||||
// javaScriptWindowObjectCleared does not get called on the
|
||||
// initial load unless some JavaScript has been executed.
|
||||
page.mainFrame()->evaluateJavaScript(QString(""));
|
||||
|
||||
app.connect(page.mainFrame(),
|
||||
SIGNAL(javaScriptWindowObjectCleared()),
|
||||
&main,
|
||||
SLOT(JavaScriptWindowObjectCleared()));
|
||||
#endif
|
||||
|
||||
if (!body.isNull())
|
||||
page.mainFrame()->load(req, method, body);
|
||||
else
|
||||
page.mainFrame()->load(req, method);
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
#include <QtWebKit>
|
||||
|
||||
class CutyCapt;
|
||||
class CutyPage : public QWebPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void setAttribute(QWebSettings::WebAttribute option, const QString& value);
|
||||
void setUserAgent(const QString& userAgent);
|
||||
void setAlertString(const QString& alertString);
|
||||
void setPrintAlerts(bool printAlerts);
|
||||
void setCutyCapt(CutyCapt* cutyCapt);
|
||||
QString getAlertString();
|
||||
|
||||
protected:
|
||||
QString chooseFile(QWebFrame *frame, const QString& suggestedFile);
|
||||
void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID);
|
||||
bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result);
|
||||
void javaScriptAlert(QWebFrame* frame, const QString& msg);
|
||||
bool javaScriptConfirm(QWebFrame* frame, const QString& msg);
|
||||
QString userAgentForUrl(const QUrl& url) const;
|
||||
QString mUserAgent;
|
||||
QString mAlertString;
|
||||
bool mPrintAlerts;
|
||||
CutyCapt* mCutyCapt;
|
||||
};
|
||||
|
||||
class CutyCapt : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
// TODO: This should really be elsewhere and be named differently
|
||||
enum OutputFormat { SvgFormat, PdfFormat, PsFormat, InnerTextFormat, HtmlFormat,
|
||||
RenderTreeFormat, PngFormat, JpegFormat, MngFormat, TiffFormat, GifFormat,
|
||||
BmpFormat, PpmFormat, XbmFormat, XpmFormat, OtherFormat };
|
||||
|
||||
CutyCapt(CutyPage* page,
|
||||
const QString& output,
|
||||
int delay,
|
||||
OutputFormat format,
|
||||
const QString& scriptProp,
|
||||
const QString& scriptCode);
|
||||
|
||||
private slots:
|
||||
void DocumentComplete(bool ok);
|
||||
void InitialLayoutCompleted();
|
||||
void JavaScriptWindowObjectCleared();
|
||||
void Timeout();
|
||||
void Delayed();
|
||||
|
||||
private:
|
||||
void TryDelayedRender();
|
||||
void saveSnapshot();
|
||||
bool mSawInitialLayout;
|
||||
bool mSawDocumentComplete;
|
||||
|
||||
protected:
|
||||
QString mOutput;
|
||||
int mDelay;
|
||||
CutyPage* mPage;
|
||||
OutputFormat mFormat;
|
||||
QObject* mScriptObj;
|
||||
QString mScriptProp;
|
||||
QString mScriptCode;
|
||||
};
|
||||
Binary file not shown.
@@ -1,10 +0,0 @@
|
||||
QT += webkit svg network
|
||||
SOURCES = CutyCapt.cpp
|
||||
HEADERS = CutyCapt.hpp
|
||||
CONFIG += qt console
|
||||
|
||||
contains(CONFIG, static): {
|
||||
QTPLUGIN += qjpeg qgif qsvg qmng qico qtiff
|
||||
DEFINES += STATIC_PLUGINS
|
||||
}
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
#############################################################################
|
||||
# Makefile for building: CutyCapt
|
||||
# Generated by qmake (2.01a) (Qt 4.8.1) on: Thu Aug 30 14:02:45 2012
|
||||
# Project: CutyCapt.pro
|
||||
# Template: app
|
||||
# Command: /usr/bin/qmake -o Makefile CutyCapt.pro
|
||||
#############################################################################
|
||||
|
||||
####### Compiler, tools and options
|
||||
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
DEFINES = -DQT_WEBKIT -DQT_NO_DEBUG -DQT_WEBKIT_LIB -DQT_SVG_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -DQT_SHARED
|
||||
CFLAGS = -pipe -O2 -D_REENTRANT -Wall -W $(DEFINES)
|
||||
CXXFLAGS = -pipe -O2 -D_REENTRANT -Wall -W $(DEFINES)
|
||||
INCPATH = -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4/QtNetwork -I/usr/include/qt4/QtGui -I/usr/include/qt4/QtSvg -I/usr/include/qt4/QtWebKit -I/usr/include/qt4 -I.
|
||||
LINK = g++
|
||||
LFLAGS = -Wl,-O1
|
||||
LIBS = $(SUBLIBS) -L/usr/lib/i386-linux-gnu -lQtWebKit -lQtSvg -lQtGui -lQtNetwork -lQtCore -lpthread
|
||||
AR = ar cqs
|
||||
RANLIB =
|
||||
QMAKE = /usr/bin/qmake
|
||||
TAR = tar -cf
|
||||
COMPRESS = gzip -9f
|
||||
COPY = cp -f
|
||||
SED = sed
|
||||
COPY_FILE = $(COPY)
|
||||
COPY_DIR = $(COPY) -r
|
||||
STRIP = strip
|
||||
INSTALL_FILE = install -m 644 -p
|
||||
INSTALL_DIR = $(COPY_DIR)
|
||||
INSTALL_PROGRAM = install -m 755 -p
|
||||
DEL_FILE = rm -f
|
||||
SYMLINK = ln -f -s
|
||||
DEL_DIR = rmdir
|
||||
MOVE = mv -f
|
||||
CHK_DIR_EXISTS= test -d
|
||||
MKDIR = mkdir -p
|
||||
|
||||
####### Output directory
|
||||
|
||||
OBJECTS_DIR = ./
|
||||
|
||||
####### Files
|
||||
|
||||
SOURCES = CutyCapt.cpp moc_CutyCapt.cpp
|
||||
OBJECTS = CutyCapt.o \
|
||||
moc_CutyCapt.o
|
||||
DIST = /usr/share/qt4/mkspecs/common/unix.conf \
|
||||
/usr/share/qt4/mkspecs/common/linux.conf \
|
||||
/usr/share/qt4/mkspecs/common/gcc-base.conf \
|
||||
/usr/share/qt4/mkspecs/common/gcc-base-unix.conf \
|
||||
/usr/share/qt4/mkspecs/common/g++-base.conf \
|
||||
/usr/share/qt4/mkspecs/common/g++-unix.conf \
|
||||
/usr/share/qt4/mkspecs/qconfig.pri \
|
||||
/usr/share/qt4/mkspecs/modules/qt_webkit_version.pri \
|
||||
/usr/share/qt4/mkspecs/features/qt_functions.prf \
|
||||
/usr/share/qt4/mkspecs/features/qt_config.prf \
|
||||
/usr/share/qt4/mkspecs/features/exclusive_builds.prf \
|
||||
/usr/share/qt4/mkspecs/features/default_pre.prf \
|
||||
/usr/share/qt4/mkspecs/features/release.prf \
|
||||
/usr/share/qt4/mkspecs/features/default_post.prf \
|
||||
/usr/share/qt4/mkspecs/features/qt.prf \
|
||||
/usr/share/qt4/mkspecs/features/unix/thread.prf \
|
||||
/usr/share/qt4/mkspecs/features/moc.prf \
|
||||
/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \
|
||||
/usr/share/qt4/mkspecs/features/warn_on.prf \
|
||||
/usr/share/qt4/mkspecs/features/resources.prf \
|
||||
/usr/share/qt4/mkspecs/features/uic.prf \
|
||||
/usr/share/qt4/mkspecs/features/yacc.prf \
|
||||
/usr/share/qt4/mkspecs/features/lex.prf \
|
||||
/usr/share/qt4/mkspecs/features/include_source_dir.prf \
|
||||
CutyCapt.pro
|
||||
QMAKE_TARGET = CutyCapt
|
||||
DESTDIR =
|
||||
TARGET = CutyCapt
|
||||
|
||||
first: all
|
||||
####### Implicit rules
|
||||
|
||||
.SUFFIXES: .o .c .cpp .cc .cxx .C
|
||||
|
||||
.cpp.o:
|
||||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
|
||||
|
||||
.cc.o:
|
||||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
|
||||
|
||||
.cxx.o:
|
||||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
|
||||
|
||||
.C.o:
|
||||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<"
|
||||
|
||||
####### Build rules
|
||||
|
||||
all: Makefile $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)
|
||||
|
||||
Makefile: CutyCapt.pro /usr/share/qt4/mkspecs/linux-g++/qmake.conf /usr/share/qt4/mkspecs/common/unix.conf \
|
||||
/usr/share/qt4/mkspecs/common/linux.conf \
|
||||
/usr/share/qt4/mkspecs/common/gcc-base.conf \
|
||||
/usr/share/qt4/mkspecs/common/gcc-base-unix.conf \
|
||||
/usr/share/qt4/mkspecs/common/g++-base.conf \
|
||||
/usr/share/qt4/mkspecs/common/g++-unix.conf \
|
||||
/usr/share/qt4/mkspecs/qconfig.pri \
|
||||
/usr/share/qt4/mkspecs/modules/qt_webkit_version.pri \
|
||||
/usr/share/qt4/mkspecs/features/qt_functions.prf \
|
||||
/usr/share/qt4/mkspecs/features/qt_config.prf \
|
||||
/usr/share/qt4/mkspecs/features/exclusive_builds.prf \
|
||||
/usr/share/qt4/mkspecs/features/default_pre.prf \
|
||||
/usr/share/qt4/mkspecs/features/release.prf \
|
||||
/usr/share/qt4/mkspecs/features/default_post.prf \
|
||||
/usr/share/qt4/mkspecs/features/qt.prf \
|
||||
/usr/share/qt4/mkspecs/features/unix/thread.prf \
|
||||
/usr/share/qt4/mkspecs/features/moc.prf \
|
||||
/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf \
|
||||
/usr/share/qt4/mkspecs/features/warn_on.prf \
|
||||
/usr/share/qt4/mkspecs/features/resources.prf \
|
||||
/usr/share/qt4/mkspecs/features/uic.prf \
|
||||
/usr/share/qt4/mkspecs/features/yacc.prf \
|
||||
/usr/share/qt4/mkspecs/features/lex.prf \
|
||||
/usr/share/qt4/mkspecs/features/include_source_dir.prf \
|
||||
/usr/lib/i386-linux-gnu/libQtWebKit.prl \
|
||||
/usr/lib/i386-linux-gnu/libQtSvg.prl \
|
||||
/usr/lib/i386-linux-gnu/libQtGui.prl \
|
||||
/usr/lib/i386-linux-gnu/libQtNetwork.prl \
|
||||
/usr/lib/i386-linux-gnu/libQtCore.prl
|
||||
$(QMAKE) -o Makefile CutyCapt.pro
|
||||
/usr/share/qt4/mkspecs/common/unix.conf:
|
||||
/usr/share/qt4/mkspecs/common/linux.conf:
|
||||
/usr/share/qt4/mkspecs/common/gcc-base.conf:
|
||||
/usr/share/qt4/mkspecs/common/gcc-base-unix.conf:
|
||||
/usr/share/qt4/mkspecs/common/g++-base.conf:
|
||||
/usr/share/qt4/mkspecs/common/g++-unix.conf:
|
||||
/usr/share/qt4/mkspecs/qconfig.pri:
|
||||
/usr/share/qt4/mkspecs/modules/qt_webkit_version.pri:
|
||||
/usr/share/qt4/mkspecs/features/qt_functions.prf:
|
||||
/usr/share/qt4/mkspecs/features/qt_config.prf:
|
||||
/usr/share/qt4/mkspecs/features/exclusive_builds.prf:
|
||||
/usr/share/qt4/mkspecs/features/default_pre.prf:
|
||||
/usr/share/qt4/mkspecs/features/release.prf:
|
||||
/usr/share/qt4/mkspecs/features/default_post.prf:
|
||||
/usr/share/qt4/mkspecs/features/qt.prf:
|
||||
/usr/share/qt4/mkspecs/features/unix/thread.prf:
|
||||
/usr/share/qt4/mkspecs/features/moc.prf:
|
||||
/usr/share/qt4/mkspecs/features/unix/gdb_dwarf_index.prf:
|
||||
/usr/share/qt4/mkspecs/features/warn_on.prf:
|
||||
/usr/share/qt4/mkspecs/features/resources.prf:
|
||||
/usr/share/qt4/mkspecs/features/uic.prf:
|
||||
/usr/share/qt4/mkspecs/features/yacc.prf:
|
||||
/usr/share/qt4/mkspecs/features/lex.prf:
|
||||
/usr/share/qt4/mkspecs/features/include_source_dir.prf:
|
||||
/usr/lib/i386-linux-gnu/libQtWebKit.prl:
|
||||
/usr/lib/i386-linux-gnu/libQtSvg.prl:
|
||||
/usr/lib/i386-linux-gnu/libQtGui.prl:
|
||||
/usr/lib/i386-linux-gnu/libQtNetwork.prl:
|
||||
/usr/lib/i386-linux-gnu/libQtCore.prl:
|
||||
qmake: FORCE
|
||||
@$(QMAKE) -o Makefile CutyCapt.pro
|
||||
|
||||
dist:
|
||||
@$(CHK_DIR_EXISTS) .tmp/CutyCapt1.0.0 || $(MKDIR) .tmp/CutyCapt1.0.0
|
||||
$(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/CutyCapt1.0.0/ && $(COPY_FILE) --parents CutyCapt.hpp .tmp/CutyCapt1.0.0/ && $(COPY_FILE) --parents CutyCapt.cpp .tmp/CutyCapt1.0.0/ && (cd `dirname .tmp/CutyCapt1.0.0` && $(TAR) CutyCapt1.0.0.tar CutyCapt1.0.0 && $(COMPRESS) CutyCapt1.0.0.tar) && $(MOVE) `dirname .tmp/CutyCapt1.0.0`/CutyCapt1.0.0.tar.gz . && $(DEL_FILE) -r .tmp/CutyCapt1.0.0
|
||||
|
||||
|
||||
clean:compiler_clean
|
||||
-$(DEL_FILE) $(OBJECTS)
|
||||
-$(DEL_FILE) *~ core *.core
|
||||
|
||||
|
||||
####### Sub-libraries
|
||||
|
||||
distclean: clean
|
||||
-$(DEL_FILE) $(TARGET)
|
||||
-$(DEL_FILE) Makefile
|
||||
|
||||
|
||||
check: first
|
||||
|
||||
mocclean: compiler_moc_header_clean compiler_moc_source_clean
|
||||
|
||||
mocables: compiler_moc_header_make_all compiler_moc_source_make_all
|
||||
|
||||
compiler_moc_header_make_all: moc_CutyCapt.cpp
|
||||
compiler_moc_header_clean:
|
||||
-$(DEL_FILE) moc_CutyCapt.cpp
|
||||
moc_CutyCapt.cpp: CutyCapt.hpp
|
||||
/usr/bin/moc-qt4 $(DEFINES) $(INCPATH) CutyCapt.hpp -o moc_CutyCapt.cpp
|
||||
|
||||
compiler_rcc_make_all:
|
||||
compiler_rcc_clean:
|
||||
compiler_image_collection_make_all: qmake_image_collection.cpp
|
||||
compiler_image_collection_clean:
|
||||
-$(DEL_FILE) qmake_image_collection.cpp
|
||||
compiler_moc_source_make_all:
|
||||
compiler_moc_source_clean:
|
||||
compiler_uic_make_all:
|
||||
compiler_uic_clean:
|
||||
compiler_yacc_decl_make_all:
|
||||
compiler_yacc_decl_clean:
|
||||
compiler_yacc_impl_make_all:
|
||||
compiler_yacc_impl_clean:
|
||||
compiler_lex_make_all:
|
||||
compiler_lex_clean:
|
||||
compiler_clean: compiler_moc_header_clean
|
||||
|
||||
####### Compile
|
||||
|
||||
CutyCapt.o: CutyCapt.cpp CutyCapt.hpp
|
||||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o CutyCapt.o CutyCapt.cpp
|
||||
|
||||
moc_CutyCapt.o: moc_CutyCapt.cpp
|
||||
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_CutyCapt.o moc_CutyCapt.cpp
|
||||
|
||||
####### Install
|
||||
|
||||
install: FORCE
|
||||
|
||||
uninstall: FORCE
|
||||
|
||||
FORCE:
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
/****************************************************************************
|
||||
** Meta object code from reading C++ file 'CutyCapt.hpp'
|
||||
**
|
||||
** Created: Thu Aug 30 14:03:03 2012
|
||||
** by: The Qt Meta Object Compiler version 63 (Qt 4.8.1)
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
*****************************************************************************/
|
||||
|
||||
#include "CutyCapt.hpp"
|
||||
#if !defined(Q_MOC_OUTPUT_REVISION)
|
||||
#error "The header file 'CutyCapt.hpp' doesn't include <QObject>."
|
||||
#elif Q_MOC_OUTPUT_REVISION != 63
|
||||
#error "This file was generated using the moc from 4.8.1. It"
|
||||
#error "cannot be used with the include files from this version of Qt."
|
||||
#error "(The moc has changed too much.)"
|
||||
#endif
|
||||
|
||||
QT_BEGIN_MOC_NAMESPACE
|
||||
static const uint qt_meta_data_CutyPage[] = {
|
||||
|
||||
// content:
|
||||
6, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
0, 0, // methods
|
||||
0, 0, // properties
|
||||
0, 0, // enums/sets
|
||||
0, 0, // constructors
|
||||
0, // flags
|
||||
0, // signalCount
|
||||
|
||||
0 // eod
|
||||
};
|
||||
|
||||
static const char qt_meta_stringdata_CutyPage[] = {
|
||||
"CutyPage\0"
|
||||
};
|
||||
|
||||
void CutyPage::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
|
||||
{
|
||||
Q_UNUSED(_o);
|
||||
Q_UNUSED(_id);
|
||||
Q_UNUSED(_c);
|
||||
Q_UNUSED(_a);
|
||||
}
|
||||
|
||||
const QMetaObjectExtraData CutyPage::staticMetaObjectExtraData = {
|
||||
0, qt_static_metacall
|
||||
};
|
||||
|
||||
const QMetaObject CutyPage::staticMetaObject = {
|
||||
{ &QWebPage::staticMetaObject, qt_meta_stringdata_CutyPage,
|
||||
qt_meta_data_CutyPage, &staticMetaObjectExtraData }
|
||||
};
|
||||
|
||||
#ifdef Q_NO_DATA_RELOCATION
|
||||
const QMetaObject &CutyPage::getStaticMetaObject() { return staticMetaObject; }
|
||||
#endif //Q_NO_DATA_RELOCATION
|
||||
|
||||
const QMetaObject *CutyPage::metaObject() const
|
||||
{
|
||||
return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
|
||||
}
|
||||
|
||||
void *CutyPage::qt_metacast(const char *_clname)
|
||||
{
|
||||
if (!_clname) return 0;
|
||||
if (!strcmp(_clname, qt_meta_stringdata_CutyPage))
|
||||
return static_cast<void*>(const_cast< CutyPage*>(this));
|
||||
return QWebPage::qt_metacast(_clname);
|
||||
}
|
||||
|
||||
int CutyPage::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
||||
{
|
||||
_id = QWebPage::qt_metacall(_c, _id, _a);
|
||||
if (_id < 0)
|
||||
return _id;
|
||||
return _id;
|
||||
}
|
||||
static const uint qt_meta_data_CutyCapt[] = {
|
||||
|
||||
// content:
|
||||
6, // revision
|
||||
0, // classname
|
||||
0, 0, // classinfo
|
||||
5, 14, // methods
|
||||
0, 0, // properties
|
||||
0, 0, // enums/sets
|
||||
0, 0, // constructors
|
||||
0, // flags
|
||||
0, // signalCount
|
||||
|
||||
// slots: signature, parameters, type, tag, flags
|
||||
13, 10, 9, 9, 0x08,
|
||||
36, 9, 9, 9, 0x08,
|
||||
61, 9, 9, 9, 0x08,
|
||||
93, 9, 9, 9, 0x08,
|
||||
103, 9, 9, 9, 0x08,
|
||||
|
||||
0 // eod
|
||||
};
|
||||
|
||||
static const char qt_meta_stringdata_CutyCapt[] = {
|
||||
"CutyCapt\0\0ok\0DocumentComplete(bool)\0"
|
||||
"InitialLayoutCompleted()\0"
|
||||
"JavaScriptWindowObjectCleared()\0"
|
||||
"Timeout()\0Delayed()\0"
|
||||
};
|
||||
|
||||
void CutyCapt::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
|
||||
{
|
||||
if (_c == QMetaObject::InvokeMetaMethod) {
|
||||
Q_ASSERT(staticMetaObject.cast(_o));
|
||||
CutyCapt *_t = static_cast<CutyCapt *>(_o);
|
||||
switch (_id) {
|
||||
case 0: _t->DocumentComplete((*reinterpret_cast< bool(*)>(_a[1]))); break;
|
||||
case 1: _t->InitialLayoutCompleted(); break;
|
||||
case 2: _t->JavaScriptWindowObjectCleared(); break;
|
||||
case 3: _t->Timeout(); break;
|
||||
case 4: _t->Delayed(); break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QMetaObjectExtraData CutyCapt::staticMetaObjectExtraData = {
|
||||
0, qt_static_metacall
|
||||
};
|
||||
|
||||
const QMetaObject CutyCapt::staticMetaObject = {
|
||||
{ &QObject::staticMetaObject, qt_meta_stringdata_CutyCapt,
|
||||
qt_meta_data_CutyCapt, &staticMetaObjectExtraData }
|
||||
};
|
||||
|
||||
#ifdef Q_NO_DATA_RELOCATION
|
||||
const QMetaObject &CutyCapt::getStaticMetaObject() { return staticMetaObject; }
|
||||
#endif //Q_NO_DATA_RELOCATION
|
||||
|
||||
const QMetaObject *CutyCapt::metaObject() const
|
||||
{
|
||||
return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
|
||||
}
|
||||
|
||||
void *CutyCapt::qt_metacast(const char *_clname)
|
||||
{
|
||||
if (!_clname) return 0;
|
||||
if (!strcmp(_clname, qt_meta_stringdata_CutyCapt))
|
||||
return static_cast<void*>(const_cast< CutyCapt*>(this));
|
||||
return QObject::qt_metacast(_clname);
|
||||
}
|
||||
|
||||
int CutyCapt::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
||||
{
|
||||
_id = QObject::qt_metacall(_c, _id, _a);
|
||||
if (_id < 0)
|
||||
return _id;
|
||||
if (_c == QMetaObject::InvokeMetaMethod) {
|
||||
if (_id < 5)
|
||||
qt_static_metacall(this, _c, _id, _a);
|
||||
_id -= 5;
|
||||
}
|
||||
return _id;
|
||||
}
|
||||
QT_END_MOC_NAMESPACE
|
||||
Binary file not shown.
@@ -1,12 +0,0 @@
|
||||
HowTo Install:
|
||||
|
||||
% sudo apt-get install subversion libqt4-webkit libqt4-dev g++
|
||||
% svn co https://cutycapt.svn.sourceforge.net/svnroot/cutycapt
|
||||
% cd cutycapt/CutyCapt
|
||||
% qmake
|
||||
% make
|
||||
|
||||
HowTo Use:
|
||||
% ./CutyCapt --url=http://www.example.org --out=example.png
|
||||
|
||||
|
||||
@@ -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
-39
@@ -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
|
||||
@@ -204,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
|
||||
@@ -214,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
|
||||
@@ -224,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
|
||||
@@ -287,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 )
|
||||
@@ -302,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 ):
|
||||
@@ -326,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)
|
||||
@@ -344,7 +346,7 @@ 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']
|
||||
@@ -368,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()
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# A Python Library to create a Progress Bar.
|
||||
# Copyright (C) 2008 BJ Dierkes <wdierkes@5dollarwhitebox.org>
|
||||
@@ -96,7 +96,7 @@ def bufcount(filename):
|
||||
if not os.path.exists(filename):
|
||||
raise Exception
|
||||
try:
|
||||
fh = open(filename)
|
||||
fh = open(filename, encoding='utf-8', errors='replace')
|
||||
lines = 0
|
||||
buf_size = 1024 * 1024
|
||||
read_f = fh.read # loop optimization
|
||||
|
||||
+43
-25
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python3.2
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__version__ = "0.0.1"
|
||||
@@ -11,17 +11,23 @@ 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)
|
||||
config.read(cfg_path, encoding='utf-8')
|
||||
trans = dict(config.items(sec_name))
|
||||
return dict([[v,k] for k,v in trans.items()])
|
||||
|
||||
@@ -42,12 +48,14 @@ def convertSVG(in_path,filename,pic_config):
|
||||
if options.verbose:
|
||||
print (filename, 'already exist!')
|
||||
else:
|
||||
background = " -background " + pic_config['background'] + " "
|
||||
resize = " -resize " + pic_config['width'] + "x" + pic_config['height'] + " "
|
||||
density = " -density " + pic_config['density']
|
||||
cmd = "convert"
|
||||
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(cmd + density + background + inpath + resize + jpgpath, shell = True)
|
||||
subprocess.check_call(cmdline)
|
||||
if options.verbose:
|
||||
print ('start converting', filename)
|
||||
Epb.slide_progbar()
|
||||
@@ -111,17 +119,18 @@ font-weight:normal;font-stretch:normal;font-family:Ubuntu;\
|
||||
|
||||
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('$PICCREATE_OUT')
|
||||
if options.svgfolder:
|
||||
work_path = os.path.expandvars(options.svgfolder)
|
||||
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,
|
||||
'format' : options.format or 'jpg',
|
||||
'density' : '150',
|
||||
'background' : 'white',
|
||||
'folder' : work_path,
|
||||
@@ -144,14 +153,19 @@ def main():
|
||||
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', 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)
|
||||
config.read(config_path, encoding='utf-8')
|
||||
# fill the config
|
||||
pic_config = dict(config.items(options.format))
|
||||
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:
|
||||
@@ -165,7 +179,7 @@ def main():
|
||||
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', 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
|
||||
@@ -173,7 +187,7 @@ def main():
|
||||
print ("you must give a config file for checking the svg files")
|
||||
return None
|
||||
svglist = []
|
||||
with open(csv_path, newline='') as csvfile:
|
||||
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:
|
||||
@@ -210,14 +224,17 @@ Read Confg, create wakeup and scale this kind of jiffy box
|
||||
parser.add_option("-b", "--background", dest="background",
|
||||
help="set the backgroundcolor. 'none' for transparent",
|
||||
action='store')
|
||||
parser.add_option("-c", "--config", dest="config",
|
||||
help="config file with all data for jobs",
|
||||
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 alreads exist",
|
||||
help="create the pictures, no matter if they already exist",
|
||||
action='store_true')
|
||||
parser.add_option("--format", dest="format", default='jpg',
|
||||
help="format of the picture",
|
||||
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",
|
||||
@@ -228,8 +245,10 @@ Read Confg, create wakeup and scale this kind of jiffy box
|
||||
parser.add_option("-p", "--progress", dest="progress",
|
||||
help="print a progression bar",
|
||||
action='store_true')
|
||||
parser.add_option("-s", "--svgfolder", dest="svgfolder",
|
||||
help="path to the folder with the svg-files",
|
||||
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",
|
||||
@@ -238,8 +257,7 @@ Read Confg, create wakeup and scale this kind of jiffy box
|
||||
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 success",
|
||||
help="dont generate images, just try if the conversion would succeed",
|
||||
action='store_true')
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
+18
-10
@@ -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 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
@@ -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:
|
||||
@@ -0,0 +1 @@
|
||||
playwright
|
||||
Reference in New Issue
Block a user