snibgo's ImageMagick pages

SVG text

ImageMagick is a wonderful non-interactive tool for processing raster images. With Inkscape, it can do fancy things with text in SVG files.

We can use SVG (Scalar Vector Graphics) files to write text on a path or to flow within areas. This uses features of SVG that ImageMagick can't currently process, so we need to use Inkscape as the SVG delegate.

We will use Inkscape to create curves and areas by hand, then IM convert will drive Inkscape to render the text, and IM will compose this over the raster image.

The processes described here use some unpublished programs of my own. Their functionality could be replicated fairly easily, using sed or similar.

For details of SVG syntax and semantics, see SGV1.1 8:Paths, SVG1.1 10.13:Text on a path and SVG1.2 4:Flowing text and graphics.

Caution: Flowing text is defined in SVG 1.2, which is a draft standard. If and when the standard is aproved, the syntax may change, and Inkscape would also change, and this page may also need to change.

Installation

Install Gimp and Potrace.

Install Inkscape from Inkscape.org. Edit delegates.xml, or create a personal delegates.xml. On Windows I have %USERPROFILE%\.magick\delegates.xml:

<?xml version="1.0" encoding="UTF-8"?>
<delegatemap>
  <delegate decode="flip" command="convert &quot;%i&quot; -flip &quot;miff:%o&quot;"/>
  <delegate encode="flip" command="convert &quot;%i&quot; -flip &quot;miff:%o&quot;"/>
  <delegate decode="svg:decode" stealth="True" command="&quot;inkscape.exe&quot; -z &quot;%s&quot; --export-png=&quot;%s&quot; --export-dpi=&quot;%s&quot; --export-background=&quot;%s&quot; --export-background-opacity=&quot;%s&quot; &gt; &quot;%s&quot; 2&gt;&1"/>
  <delegate decode="crxz:decode" encode="tiff" command="dcraw -6 -T -O &quot;%o&quot; &quot;%i&quot;"/>
  <delegate decode="dng2:decode" command="dcraw.exe -q 2 -6 -w -O &quot;%u.ppm&quot; &quot;%i&quot;"/>
  <delegate decode="png" encode="msphx" command="C:/WINDOWS/System32/rundll32.exe &quot;C:/Program Files/Windows Photo Viewer/PhotoViewer.dll&quot;, ImageView_Fullscreen %i"/>
  <delegate decode="png" encode="msph" command="msph.bat %i"/>
  <delegate decode="png" encode="gimp" command="&quot;C:/Program Files/gimp 2/bin/gimp-2.8.exe&quot; %i"/>
</delegatemap>

We don't need flip and flop; I just put those in for fun.

Put the Inkscape directory in the system path.

call %PICTBAT%setInkPath

Inkscape can be run directly from the command line, eg:

inkscape -f in.svg -e out.png -y 1

On this page, I run Inkscape indirectly, as a delegate of ImageMagick.

Text on paths

For the source image, I will use a crop from an image made in the Adding zing to photographs page. All operations will be performed on this large image, but the results will be shrunk for this web page.

set RESIZE=-resize 500x500

%IM%convert ^
  zp_sus_sat.tiff ^
  -crop 4924x4155+0+3223 +repage ^
  -write st_src.tiff ^
  %RESIZE% ^
  st_src_sm.jpg
st_src_sm.jpg

ImageMagick could convert this to an SVG file, but it would create one vector circle for every pixel. This takes masses of space and processing time. Instead, we will embed the raster image within the SVG file.

Inkscape can render vector graphics over raster images, though it only writes 8 bits/channel. Instead, I use Inkscape to render text and ImageMagick to composite that over raster images.

Create an SVG file that links to the full-size raster image st_src.tiff. This can be done by starting Inkscape, importing the image (file, import, link not embed), and saving as an SVG which I will call st_limbs.svg. Close Inkscape. Open the SVG file in a text editor (eg Wordpad). Edit the SVG for the widths, heights and offsets.

The image will be towards the bottom of the file, something like this:

 <image
       y="-1545.1378"
       x="-2087"
       id="image2993"
       xlink:href="file:///F:/prose/PICTURES/st_src.tiff"
       height="4155"
       width="4924" />

Change x and y values to 0 (zero). Further up the file, change inkscape:window-width and inkscape:window-height to the raster dimensions, 4155 and 4924. Near the top, within the SVG tag, change width and height to the raster dimensions.

st_limbs.svg now contains:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   width="4924"
   height="4155"
   id="svg2"
   version="1.1"
   inkscape:version="0.48.4 r9939"
   sodipodi:docname="New document 1">
  <defs
     id="defs4" />
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="0.35"
     inkscape:cx="375"
     inkscape:cy="520"
     inkscape:document-units="px"
     inkscape:current-layer="layer1"
     showgrid="false"
     inkscape:window-width="4924"
     inkscape:window-height="4155"
     inkscape:window-x="-8"
     inkscape:window-y="-8"
     inkscape:window-maximized="1" />
  <metadata
     id="metadata7">
    <rdf:RDF>
      <cc:Work
         rdf:about="">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1">
    <image
       y="0"
       x="0"
       id="image2993"
       xlink:href="file:///F:/prose/PICTURES/st_src.tiff"
       height="4155"
       width="4924" />
  </g>
</svg>

ASIDE: IM's rasterisation of SVG, via Inkscape, is broken in v6.0.0-0, so I temporarily used v6.8.9-5. This is now fixed. However, the formatted text facilities used here don't work when Inkscape is called from IM, so for these I need to call Inkscape directly.

Use IM to compare the original raster image with the rasterised vector:

rem call %PICTBAT%svg2png st_limbs.svg st_temp.png
rem %IM%compare -verbose -metric RMSE st_src.tiff st_temp.png NULL: 
rem del st_temp.png

rem %IMSVG%convert -verbose st_limbs.svg st_svg_tmp.png

rem %IMSVG%convert -verbose st_svg_tmp.png st_src.tiff -metric RMSE -compare -format %%[distortion] info: 

%IM%convert -verbose st_limbs.svg st_src.tiff -metric RMSE -compare -format %%[distortion] info: 
DPI: 90
Background RRGGBBAA: ffffffff
Area 0:0:4924:4155 exported to 4924 x 4155 pixels (90 dpi)
Bitmap saved as: C:/Users/Alan/AppData/Local/Temp/magick-3528Drx67B33uDvN
0.00113187

This tests whether IM can run Inkscape, Inkscape can process the edited SVG file, and we get back what we put in.

The difference should be small (the numbers in parentheses should be less than 0.01). Inkscape creates PNG files with 8 bits/channel/pixel, so we don't expect an exact match.

(After running Inkscape from the command line, I get the error message "RegistryTool: Could not set the value 'C:\ProgramFiles\Inkscape\inkscape.exe'". This doesn't seem to cause a problem.)

In Inkscape, read this SVG file. View, Zoom to the page. Create a new layer (Layer, Add layer, name: "Paths", Above current). Create the desired paths ("Draw Bezier curves and straight lines"). If the lines are hard to see against the raster image, try changing the opacity of the image to about 40% (Object, Fill and stroke, Opacity). To help clarity, I set stroke width to 1mm, with a Start Marker of TriangleOutL. For each path, edit "object properties" to change the id to something meaningful and unique; use filenaming conventions, eg don't use spaces. Save the SVG file, eg as st_limbs2.svg.

Just for fun, what does it look like?

%IM%convert ^
  st_limbs2.svg ^
  %RESIZE% ^
  st_limbs2_sm.jpg
st_limbs2_sm.jpg

Extract the curve paths. This could be done by hand, but my program RdXml does the job. The result, curves.s, can be included within the defs section of an SVG file. Then one or more curves can be used (with xlink) as required. We don't care about anything else in st_limbs2.svg.

RdXml /ist_limbs2.svg /n /v /ccurves.s

Here is curves.s:

<path id="path3961"
d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
 />
<path id="path3819"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
 />
<path id="rightArm"
d="m 3943.0288,771.27842 c 99.1868,-211.89901 94.6782,-184.84808 139.7631,-383.22162 45.0849,-198.37354 40.5764,-333.628219 40.5764,-333.628219"
 />
<path id="leftLeg"
d="m 3590.0322,2147.955 c 157.7971,338.1367 254.9672,550.6545 401.2556,802.5111 146.2884,251.8566 113.8147,425.3834 238.9499,689.799 143.4977,303.2163 279.5263,473.3914 279.5263,473.3914"
 />
<path id="rightLeg"
d="m 2054.9628,4087.2823 c 111.2433,-277.4071 283.6244,-569.5646 435.2041,-849.1977 139.5902,-257.5149 320.0689,-553.5677 436.536,-762.647 116.4671,-209.0793 332.4999,-623.9811 332.4999,-623.9811"
 />
<path id="donkeyLeg"
d="m 404.06881,2641.4554 c 246.86762,268.4415 499.49618,490.9478 602.18109,651.7744 102.6849,160.8266 378.7132,798.0027 378.7132,798.0027"
 />

We use a template SVG file, fill in the blanks, and convert it.

FOR /F "usebackq" %%L ^
IN (`%IM%identify -format "WW=%%w\nHH=%%h" st_src.tiff`) ^
DO set %%L

ExpAt /p0 /i%PICTBAT%svgCurveTemplate.txt /ost_curve1.svg

chStrs /ist_curve1.svg /fCURVE_WW /t%WW%
chStrs /ist_curve1.svg /fCURVE_HH /t%HH%
chStrs /ist_curve1.svg /fCURVE_ID /tleftLeg
chStrs /ist_curve1.svg /fCURVE_TEXT /t"We go up, then we go down, then up again"

rem %IMSVG%convert ^
rem   -background None ^
rem   st_curve1.svg ^
rem   -write st_curve1.png ^
rem   %RESIZE% ^
rem   st_curve1_sm.png

call %PICTBAT%svg2png st_curve1.svg

%IM%convert ^
  st_curve1.png ^
  %RESIZE% ^
  st_curve1_sm.png
st_curve1_sm.png

Just for fun, put this small version of the text over the small version of the image with paths.

%IM%convert ^
  st_limbs2_sm.jpg ^
  st_curve1_sm.png ^
  -composite ^
  st_curve1_over_sm.jpg
st_curve1_over_sm.jpg

Writing multiple text curves would be efficient by creating one SVG file with all the textPath entities. Here, we take the less efficient route: create multiple SVG files, convert them all in a single convert command that will call InkScape multiple times, and composite the results. We already have the first SVG file, and the full-size PNG file made from it.

We also halve the opacity of the text.

ExpAt /p0 /i%PICTBAT%svgCurveTemplate.txt /ost_curve2.svg
ExpAt /p0 /i%PICTBAT%svgCurveTemplate.txt /ost_curve3.svg
ExpAt /p0 /i%PICTBAT%svgCurveTemplate.txt /ost_curve4.svg

chStrs /ist_curve2.svg /fCURVE_WW /t%WW%
chStrs /ist_curve2.svg /fCURVE_HH /t%HH%
chStrs /ist_curve2.svg /fCURVE_ID /trightLeg
chStrs /ist_curve2.svg /fCURVE_TEXT /t"We go up, then we go down, then up again"

chStrs /ist_curve3.svg /fCURVE_WW /t%WW%
chStrs /ist_curve3.svg /fCURVE_HH /t%HH%
chStrs /ist_curve3.svg /fCURVE_ID /trightArm
chStrs /ist_curve3.svg /fCURVE_TEXT /t"We go up, then we go down, then up again"

chStrs /ist_curve4.svg /fCURVE_WW /t%WW%
chStrs /ist_curve4.svg /fCURVE_HH /t%HH%
chStrs /ist_curve4.svg /fCURVE_ID /tdonkeyLeg
chStrs /ist_curve4.svg /fCURVE_TEXT /t"We go up, then we go down, then up again"

%IM%convert ^
  st_src.tiff ^
  ( st_curve1.png -channel A -evaluate Multiply 0.5 ) ^
  -background None ^
  ( st_curve2.svg -channel A -evaluate Multiply 0.5 ) ^
  ( st_curve3.svg -channel A -evaluate Multiply 0.5 ) ^
  ( st_curve4.svg -channel A -evaluate Multiply 0.5 ) ^
  -layers flatten ^
  -write st_curves.tiff ^
  %RESIZE% ^
  st_curves_sm.jpg
st_curves_sm.jpg

Aside: how do we find the best font size? We can overflow a path, use a large font size, and test whether we have written into the overflow area. To do this, we need to find an area on the image where the path cannot write, then extend the path to that area. This is easy manually: we choose an area top-left. To do this automatically, it may be easier to create a larger image and define the overflow in that extended area.

For example, we add an extra segment to the right leg path, double the font size, and we can see that text has been written to this area.

However, the SVG documentation doesn't seem to say how text should be written on a segmented path.

cGrep /icurves.s /ost_over.s /srightLeg /t"/"
chStrs /ist_over.s /M3 /f\q /t_
chStrs /ist_over.s /M1 /f\q /t" M0,1000 L1000,1000\q"
chStrs /ist_over.s /M3 /f_ /t\q

chStrs ^
  /i%PICTBAT%svgCurveTemplate.txt ^
  /ost_curve_over.svg ^
  /fcurves.s /tst_over.s
ExpAt /p0 /ist_curve_over.svg /ost_curve_over.svg
chStrs /ist_curve_over.svg /fCURVE_WW /t%WW%
chStrs /ist_curve_over.svg /fCURVE_HH /t%HH%
chStrs /ist_curve_over.svg /fCURVE_ID /trightLeg
chStrs ^
  /ist_curve_over.svg ^
  /ffont-size=\q100\q ^
  /tfont-size=\q200\q
chStrs ^
  /ist_curve_over.svg ^
  /fCURVE_TEXT ^
  /t"We go up, then we go down, then up again"

rem %IMSVG%convert ^
rem   -background None ^
rem   st_curve_over.svg ^
rem   %RESIZE% ^
rem   st_curve_over_sm.png

call %PICTBAT%svg2png st_curve_over.svg

%IM%convert ^
  st_curve_over.png ^
  %RESIZE% ^
  st_curve_over_sm.png
st_curve_over_sm.png

Here is st_over.s:

<path id="rightLeg"
d="m 2054.9628,4087.2823 c 111.2433,-277.4071 283.6244,-569.5646 435.2041,-849.1977 139.5902,-257.5149 320.0689,-553.5677 436.536,-762.647 116.4671,-209.0793 332.4999,-623.9811 332.4999,-623.9811 M0,1000 L1000,1000"
 />

Here is st_curve_over.svg:

<svg xmlns:svg="http://www.w3.org/2000/svg" version="1.2"
     xmlns:xlink="http://www.w3.org/1999/xlink" 
  width="100%" height="100%" viewBox="0 0 4924 4155">
  <title>Template for curved text</title>
  <defs>
<path id="rightLeg"
d="m 2054.9628,4087.2823 c 111.2433,-277.4071 283.6244,-569.5646 435.2041,-849.1977 139.5902,-257.5149 320.0689,-553.5677 436.536,-762.647 116.4671,-209.0793 332.4999,-623.9811 332.4999,-623.9811 M0,1000 L1000,1000"
 />
  </defs>
<text font-family="Verdana" font-size="200" fill="blue" >
    <textPath xlink:href="#rightLeg"
>We go up, then we go down, then up again</textPath>
  </text>
</svg>

This could be scripted and automated, with a binary chop finding the largest font that doesn't overflow.

Text in areas

SVG text can be constrained to be within a defined area. We could define the area using the method above, with Inkscape. Instead, we will use Gimp and potrace.

Read the source image in Gimp. Create a new transparent layer for each area. Name each layer suitably. Define the area by drawing around it with a black pencil, and fill the inside with black. (A pen would create anti-aliasing so it won't fill properly.) Save the file, eg as st_limbs.xcf.

Extract each image from st_limbs.xcf. See Gimp and IM.

call %PICTBAT%extrXcfLayers st_limbs.xcf

This creates a png from each layer, and a CSV (Comma-Separated Values) text file st_limbs_layers.lis:

Xcf,Layer,Opacity,Mode,Visible
f:\prose\PICTURES\st_limbs,leftArm,100.0,0,1
f:\prose\PICTURES\st_limbs,rightArm,100.0,0,1
f:\prose\PICTURES\st_limbs,shorts,100.0,0,1
f:\prose\PICTURES\st_limbs,leftLeg,100.0,0,0
f:\prose\PICTURES\st_limbs,rightLeg,100.0,0,0
f:\prose\PICTURES\st_limbs,donkeyLeg,100.0,0,0
f:\prose\PICTURES\st_limbs,donkey,100.0,0,1
f:\prose\PICTURES\st_limbs,legs_jpg,100.0,0,1

We don't need the last png in this list, so delete it:

for /F "skip=1 tokens=1,2 delims=," %%A ^
in (st_limbs_layers.lis) ^
do (
  set BASE=%%A
  set LAYER=%%B
)
del %BASE%_%LAYER%.png

For each of the extracted images, apart from the base: trace the outline with potrace, which needs pnm input; create an SVG from a template; render text within the path into a PNG.

del areas.s
del st_areas.lis

for /F "skip=1 tokens=1,2 delims=," %%A ^
in (st_limbs_layers.lis) ^
do (
  set BASELAY=%%A_%%B
  set FILENAME=%%A_%%B.png
  if exist !FILENAME! (
    echo Found !FILENAME!
    %IM%convert !FILENAME! -background White -layers flatten !BASELAY!.pnm
    %POTRACEDIR%potrace -s -o !BASELAY!.svg !BASELAY!.pnm
    del !BASELAY!.pnm
    RdXml /i!BASELAY!.svg /oNUL /v /cst_po_%%B.svg /D%%B
    chStrs /p0 /ist_po_%%B.svg /M1 /fpath /t"path transform=\qtranslate(0,%HH%) scale(0.1,-0.1)\q"
    echo @st_po_%%B.svg>>areas.s

    chStrs /p0 /i%PICTBAT%svgAreaTemplate.txt /ost_area_%%B.svg /M1 /X- /fAREA_TEXT /t"@%PICTBAT%pandp.txt"
    ExpAt  /p0 /ist_area_%%B.svg /ost_area_%%B.svg
    chBin  /p0 /ist_area_%%B.svg /f^>-\r\n /t^>
    chBin  /p0 /ist_area_%%B.svg /f\r\n-^< /t^<
    chStrs /p0 /ist_area_%%B.svg /fAREA_WW /t%WW%
    chStrs /p0 /ist_area_%%B.svg /fAREA_HH /t%HH%
    chStrs /p0 /ist_area_%%B.svg /fAREA_ID /t%%B

    rem %IMSVG%convert -background None st_area_%%B.svg st_area_%%B.png

    call %PICTBAT%svg2png st_area_%%B.svg

    echo st_area_%%B.png 
  )
)

%PICTBAT%pandp.txt contains a short extract from Pride and Prejudice by Jane Austen.

Assemble all the rendered texts into a single PNG and display it:

%IM%convert ^
  @st_areas.lis ^
  -background None ^
  -layers flatten ^
  -write st_areas.png ^
  %RESIZE% ^
  st_areas_sm.png
st_areas_sm.png
%IM%convert ^
  st_src.tiff ^
  st_areas.png ^
  -layers flatten ^
  -write st_src_areas.png ^
  %RESIZE% ^
  st_src_areas_sm.jpg
st_src_areas_sm.jpg

Rotating text in areas

If we want to rotate the text in an area, we first rotate the area, and render the text within the rotated area as above. Then rotate the text back. Rotating the text back could be done with SVG transform (see SVG1.1 Transform), but I do it here with IM.

We use the right leg as an example. What angle do we need to rotate it by?

call %PICTBAT%rotMinWidth st_limbs_rightLeg.png 1

echo %rmwANG% 
65.3757 

The two rotations add a border, so we crop it back to the original size.

FOR /F "usebackq" %%L ^
IN (`%IM%identify -format "RL_WW=%%w\nRL_HH=%%h" st_limbs_rightLeg.png`) ^
DO set %%L

%IM%convert ^
  st_limbs_rightLeg.png ^
  -background White -layers flatten ^
  -rotate %rmwANG% +repage ^
  st_rightLeg_rot.pnm

FOR /F "usebackq" %%L ^
IN (`%IM%identify -format "RL_WW_ROT=%%w\nRL_HH_ROT=%%h" st_rightLeg_rot.pnm`) ^
DO set %%L

%POTRACEDIR%potrace -s -o st_rightLeg_rot_crv.svg st_rightLeg_rot.pnm

RdXml /ist_rightLeg_rot_crv.svg /oNUL /v /cst_rightLeg_rot_crv2.svg /DrightLeg

chStrs /p0 /ist_rightLeg_rot_crv2.svg /M1 /fpath /t"path transform=\qtranslate(0,%RL_HH_ROT%) scale(0.1,-0.1)\q"

echo @st_rightLeg_rot_crv2.svg>areas.s

chStrs /p0 /i%PICTBAT%svgAreaTemplate.txt /ost_area_rleg_rot.svg /M1 /X- /fAREA_TEXT /t"@%PICTBAT%pandp.txt"
ExpAt  /p0 /ist_area_rleg_rot.svg /ost_area_rleg_rot.svg
chBin  /p0 /ist_area_rleg_rot.svg /f^>-\r\n /t^>
chBin  /p0 /ist_area_rleg_rot.svg /f\r\n-^< /t^<
chStrs /p0 /ist_area_rleg_rot.svg /fAREA_WW /t%RL_WW_ROT%
chStrs /p0 /ist_area_rleg_rot.svg /fAREA_HH /t%RL_HH_ROT%
chStrs /p0 /ist_area_rleg_rot.svg /fAREA_ID /trightLeg

rem Possibly naff when angle is negative.
rem %IMSVG%convert ^
rem   -background None st_area_rleg_rot.svg ^
rem   -rotate -%rmwANG% +repage ^
rem   -gravity Center ^
rem   -crop %RL_WW%x%RL_HH%+0+0 +repage ^
rem   -write st_area_rleg_rot.png ^
rem   %RESIZE% ^
rem   st_area_rleg_rot_sm.png

call %PICTBAT%svg2png st_area_rleg_rot.svg

%IM%convert ^
  st_area_rleg_rot.png ^
  -rotate -%rmwANG% +repage ^
  -gravity Center ^
  -crop %RL_WW%x%RL_HH%+0+0 +repage ^
  -write st_area_rleg_rot.png ^
  %RESIZE% ^
  st_area_rleg_rot_sm.png

Here is the result:

st_area_rleg_rot_sm.png

Compositing over the source:

%IM%convert ^
  st_src.tiff ^
  st_area_rleg_rot.png ^
  -layers flatten ^
  -write st_rleg_src.png ^
  %RESIZE% ^
  st_rleg_src_sm.jpg
st_rleg_src_sm.jpg

The image used as input for potrace is:

%IM%convert ^
  st_rightLeg_rot.pnm ^
  %RESIZE% ^
  st_rleg_pnm_sm.png

del st_rightLeg_rot.pnm
st_rleg_pnm_sm.png

This could be given a displacement before the tracing, to make the top (or middle or bottom) a straight line. See Clut Cookbook: Cluts from graphics. The text could be rendered on this displaced area, then given the opposite displacement so the text would run parallel to the line in question, instead of in a straight line.

Cleanup

del st_*.tiff

Template files

svgCurveTemplate.txt

<svg xmlns:svg="http://www.w3.org/2000/svg" version="1.2"
     xmlns:xlink="http://www.w3.org/1999/xlink" 
  width="100%" height="100%" viewBox="0 0 CURVE_WW CURVE_HH">
  <title>Template for curved text</title>
  <defs>
@curves.s
  </defs>
<text font-family="Verdana" font-size="100" fill="blue" >
    <textPath xlink:href="#CURVE_ID"
>CURVE_TEXT</textPath>
  </text>
</svg>

svgAreaTemplate.txt

This has text-anchor="end" because I like this image with right-justification.

Also useful: text-align, which can be one of start, end, center or justify (see Scalable Vector Graphics (SVG) 1.2: Flowing text and graphics: Alignment).

<svg xmlns:svg="http://www.w3.org/2000/svg" version="1.2"
     xmlns:xlink="http://www.w3.org/1999/xlink" 
  width="100%" height="100%" viewBox="0 0 AREA_WW AREA_HH">
  <title>Template for text flowing within area</title>
  <defs>
@areas.s
  </defs>

  <flowRoot font-size="100" text-anchor="end">
    <flowRegion>
      <use xlink:href="#AREA_ID" />
    </flowRegion>
    <flowPara fill="red" fill-opacity="1" stroke-opacity="0"
>-
AREA_TEXT
-</flowPara>
  </flowRoot>
</svg>

Scripts

For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.

svg2png.bat

@if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal

rem @call echoOffSave


set INFILE=%1
set OUTFILE=%~dpn1.png

if not "%2"=="" set OUTFILE=%2

if [%s2pINK_OPT%]==[] (
  set s2pINK_OPT=--export-background^="rgb^(0%%,0%%,0%%^)" --export-background-opacity^="0"
)

echo %s2pINK_OPT%

%INK%inkscape ^
  %INFILE% ^
  --export-png=%OUTFILE% ^
  %s2pINK_OPT%


call echoRestore

endlocal & set s2pOUTFILE=%OUTFILE%

getPnmPath.bat

rem From %1.pnm,
rem makes %1.svg containing an SVG path with id=%1.
rem The path is suitable for including in defs section, for use later.

c:\potrace\potrace.exe -s -o %1.svg %1.pnm

cGrep /p0 /i%1.svg /o%1.svg /X /s"\(g" /t"</g>"

chStrs /p0 /i%1.svg /f"fill=\q#000000\q stroke=\qnone\q\)"
chStrs /p0 /i%1.svg /f"\(path"
chStrs /p0 /i%1.svg /f"\(g" /t"\(path id=\q%1\q"
chStrs /p0 /i%1.svg /f"\(/g\)"
cNoBlank /p0 /i%1.svg
del %1.BAK

rotMinWidth.bat

rem For image %1, find the angle a, -90 <= a <= +90, that minimises the trimmed width.
rem %2 is 0 (default, minimise width) or 1 (minimise height)
rem Assumes pixel at (0,0) is background colour.

@if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

set INFILE=%1
set TEMPFILE=%TEMP%\%~n1_rmw.miff

if "%2"=="1" (
  set TOKENS=tokens=2
) else (
  set TOKENS=
)


%IM%convert %INFILE% -background White -layers flatten -resize 500x500 %TEMPFILE%

rem  

for /F "usebackq" %%C ^
in (`%IM%convert %TEMPFILE% -format "%%[pixel:p{0,0}]" info:`) ^
do set ONE_PIXEL=%%C

%IM%convert %TEMPFILE% -bordercolor %ONE_PIXEL% -border 1 %TEMPFILE%

set ANG0=-90
set ANG3=90

for /F "usebackq" %%L ^
in (`%IM%convert ^
  %TEMPFILE% ^
  -background "%ONE_PIXEL%" ^
  -format "Tn=%%@\n" -write info: ^
  ^( +clone -rotate -30 -format "Tx=%%@\n" -write info: +delete ^) ^
  ^( +clone -rotate %ANG0% -format "T0=%%@\n" -write info: +delete ^) ^
  ^( +clone -rotate %ANG3% -format "T3=%%@\n" -write info: +delete ^) ^
  NULL:`) ^
do set %%L

for /F "%TOKENS% delims=x+" %%L in ("%T0%") do set W0=%%L
for /F "%TOKENS% delims=x+" %%L in ("%T3%") do set W3=%%L

set FINISHED=0

:loop

for /F "usebackq" %%L ^
in (`%IM%identify -format "ANG1=%%[fx:%ANG0%+(%ANG3%-(%ANG0%))/3]\nANG2=%%[fx:%ANG0%+2*(%ANG3%-(%ANG0%))/3]" xc:`) ^
do set %%L

for /F "usebackq" %%L ^
in (`%IM%convert ^
  %TEMPFILE% ^
  -background "%ONE_PIXEL%" ^
  ^( +clone -rotate %ANG1% -format "T1=%%@\n" -write info: +delete ^) ^
  ^( +clone -rotate %ANG2% -format "T2=%%@\n" -write info: +delete ^) ^
  NULL:`) ^
do set %%L

for /F "%TOKENS% delims=x+" %%L in ("%T1%") do set W1=%%L
for /F "%TOKENS% delims=x+" %%L in ("%T2%") do set W2=%%L

if %ANG0%==%ANG1% (
  set FINISHED=1
  goto finished
)

if %ANG2%==%ANG3% (
  set FINISHED=1
  goto finished
)

if %W0%==%W1% if %W0%==%W2% if %W0%==%W3% (
  set FINISHED=1
  goto finished
)

set FINISHED=1

if %W1% LEQ %W0% if %W1% LEQ %W2% if %W1% LEQ %W3% (
  set ANG3=%ANG2%
  set W3=%W2%
  set FINISHED=0
)

if %FINISHED%==1 if %W2% LEQ %W0% if %W2% LEQ %W1% if %W2% LEQ %W3% (
  set ANG0=%ANG1%
  set W0=%W1%
  set FINISHED=0
)

if %FINISHED%==1 if %W3% LEQ %W0% if %W3% LEQ %W1% if %W3% LEQ %W2% (
  set ANG0=%ANG2%
  set W0=%W2%
  set FINISHED=0
)

echo Ang:   %ANG0% %ANG1% %ANG2% %ANG3%
echo Width: %W0% %W1% %W2% %W3%

if %FINISHED%==0 goto loop

:finished


call echoRestore

endlocal & set rmwANG=%ANG1%

All images on this page were created by the commands shown, using:

%IM%identify -version
echo.
inkscape --version
echo.
%GIMPCONS% --version
echo.
%POTRACEDIR%potrace --version
Version: ImageMagick 6.9.1--6 Q16 x64 2015-06-20 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: Cipher DPC Modules OpenMP 
Delegates (built-in): bzlib cairo freetype jng jp2 jpeg lcms lqr openexr pangocairo png ps rsvg tiff webp xml zlib


Inkscape 0.91 r13725 (Jan 30 2015)

GNU Image Manipulation Program version 2.8.10

potrace 1.11. Copyright (C) 2001-2013 Peter Selinger.
Library version: potracelib 1.11
Default unit: inches
Default page size: letter

Source file for this web page is svgtext.h1. To re-create this web page, execute "procH1 svgtext".


This page, including the images, is my copyright. Anyone is permitted to use or adapt any of the code, scripts or images for any purpose, including commercial use.

Anyone is permitted to re-publish this page, but only for non-commercial use.

Anyone is permitted to link to this page, including for commercial use.


Page version v1.1 2-May-2014.

Page created 06-Nov-2015 09:53:05.

Copyright © 2015 Alan Gibson.