We had a patron late last week or early this week ask us whether we have a program installed on our word processing PCs to generate word searches, given a list of words. I had to tell her I was sorry, we didn't, and it bugged me... so I put this together. We have OpenOffice.org software on all of our word-processing systems (some also have other options, but OO.o is the suite that all of our word processing systems have in common), so I chose to have it generate in that format.
#!/usr/bin/perl -- -*- cperl -*- # These you must set for your system: $dictfile = "/usr/share/dict/words"; my $workdir = "wordsearch-workdir"; # Non-POSIX users (e.g., Win32) need to make additional changes, e.g., # to pathnames (as I've assumed a dirseparator of /) and possibly # other small things like that, but I've gotten it working on # ActiveState Perl on Win98, so it's definitely possible. I # supplemented it with a batch file that changes directories and at # the end opens the document in OO.o. The whole thing could be made # more portable by extracting more of those things into variables or # by using more advanced filesystem tree stuff, but for this prototype # version I haven't done that. # Some defaults: my $title = "demonstration"; # What to call this specific wordsearc +h (used in filename, so stick to alphanumeric characters). my $mingridx = 5; my $mingridy = 5; my $maxgridx = 60; # There's no max y, because it expands as necessary + to fit all the words. my $maxredo = 5; # Set to the max number of times to _redo_ the grid +. # Setting $maxredo to 0 makes easy puzzles because the grid stays cram +ped the whole time # (especially so for low values of $mingridx and $mingridy). # For really sparse puzzles, raise $mingridx and $mingridy to the desi +red puzzle size. # Any characters that you want to be used for filler, whether they're +in the words or not, put them in @extrachars: my @extrachars = ();#('A'..'Z'); my @orientations = ( # Each orientation is like this: [deltax, deltay] [ 1, 0], [ 0, 1], [-1, 0], [ 0,-1], # LTR down RTL up [ 1, 1], [ 1,-1], [-1, 1], [-1,-1], # the four major diagonals # If you set both deltax and deltay to 0 in the same orientation, y +ou may be dissatisfied with the results. # With delta values greater than 1, difficult puzzles may ensue, an +d the solution may be hard to follow. ); @words = getwords(); print "Words read in: @words\n"; @wordlist = @words; # Preserve this so we can also print it later. # Once we have the words, we want counts of how many times each letter + occurs. my %charcount = (); for ((join"",@extrachars), @words) { for (split//,$_) { ++$charcount{$_}; } } # (This info will be used later when deciding what letters to use fill +ing in the empty spots.) # Initialise some things: $gridx = $mingridx; $gridy = $mingridy; my $grown=1; my $fillcount=0; # So we go through the loop the first ti +me at least. while (($fillcount <= $maxredo) and ($grown)) { if ($fillcount) { # If we're cramped enough to have to retry, let's loosen it up a b +it... $gridy+=2; $gridx+=2 if (($gridx+1)<$maxgridx); print "Re-starting with larger grid size ($gridx by $gridy) becaus +e grid was cramped last time around.\n"; print "This is the $fillcount"."th retry (out of $maxredo possible +)\n"; } @words = @wordlist; ++$fillcount; $grown=0; undef @grid; while (scalar @words) { # while and pop instead of foreach because +sometimes we unshift a word back on (see below). $w = pop @words; # Will the word even fit in the physical dimensions of the grid? while (($gridx < length $w) and ($gridy < length $w)) { print "Cannot fit $w in $gridx by $gridy grid; grid too small; g +rowing.\n"; ++$grown; ++$gridy; ++$gridx if ($gridx<$maxgridx); } # We want to try it at all positions, in all orientations, taking # the first place where it fits, but to prevent utter predictabili +ty # we want to randomly order the positions and orientations first. # (If it fits nowhere, we'll push it back on @words and grow the g +rid.) # So, a list of all the positions/orientations... # my @posn = randomorder (map { my $y=$_; map { my $x=$_; map {[$x +,$y,$_]} @orientations } 1..$gridx } 1..$gridy); # That first try totally randomized all the positions, and the # words ended up mostly being parallel (because they fit easiest # that way). So I want to try all positions in a given # orientation first before moving on to another orientation... my @posn = map { my ($x,$y)=($$_[0],$$_[1]); map { [$x,$y,$_] } ra +ndomorder(@orientations) } randomorder (map { my $y=$_; map { my $x=$_; [$x, +$y] } 1..$gridx } 1..$gridy); # (That could be golfed down some, if legibility didn't matter, bu +t...) my $placed = undef; my $tried=0; for (@posn) { ++$tried; if (place($w,$_)) { $placed = $_; last; } } if ($placed) { my ($x,$y,$o) = @$placed; my ($xd,$yd) = @$o; print "Placed $w at position [$x,$y] orientation [$xd,$yd] (${tr +ied}th position tried)\n"; } else { push @words, $w; print "Could not place $w; no room left in the $gridx by $gridy +grid; enlarging.\n"; ++$grown; ++$gridy; ++$gridx if ($gridx<$maxgridx); } } } # Great, so we now have all the words placed. It remains to fill in # the blanks, but let's take note of the solution first: print "Final grid is $gridx by $gridy (after $fillcount retries)\n"; print "-"x(int $gridx/2-4); print "SOLUTION:"; print "-"x(int $gridx/2-4); print "\n"; for $y (1..$gridy) { for $x (1..$gridx) { print " " . ($grid[$x][$y] or " "); } print$/ } my @solutiongrid; for $x (1..$gridx) { for $y (1..$gridy) { $solutiongrid[$x][$y] = $grid[$x][$y]; } } # Okay, that gives us the solution, so we no longer have to preserve # the grid with only the solution. i.e., we can now fill in the # remaining spots: my @c = randomorder(map { my $c=$_; map {$c} 1..$charcount{$c} } keys +%charcount); for $y (1..$gridy) { for $x (1..$gridx) { if (not defined $grid[$x][$y]) { my $c = pop @c; $grid[$x][$y] = $c; unshift @c, $c; # Just in case we run out. } } } print "-"x(int $gridx/2-5); print "WORDLIST:"; print "-"x(int $gridx/2-5); print "$/@wordlist$/"; # Let's also construct the XML wordlist... my $xmlwordlist=<<"XMLWORDLIST"; <text:p text:style-name="P1">Word List:</text:p> <table:table table:name="wordlist" table:style-name="wordlist"> <table:table-column table:style-name="wordlist.A" table:number-colu +mns-repeated="3"/> XMLWORDLIST ; { my @w = sort { $b cmp $a } @wordlist; while (@w) { my ($x, $y, $z) = map {pop@w} 1..3; $xmlwordlist .= " <table:table-row> <table:table-cell table:value-type=\"string\"> <text:p text:style-name=\"Table Contents\">$x</text:p> </table:table-cell> <table:table-cell table:value-type=\"string\"> <text:p text:style-name=\"Table Contents\">$y</text:p> </table:table-cell> <table:table-cell table:value-type=\"string\"> <text:p text:style-name=\"Table Contents\">$z</text:p> </table:table-cell> </table:table-row>\n"; } } $xmlwordlist .= "</table:table>\n"; print "-"x(int $gridx/2-4); print "PUZZLE:"; print "-"x(int $gridx/2-4); print "\n"; for $y (1..$gridy) { for $x (1..$gridx) { print " " . ($grid[$x][$y] or " "); } print$/ } mkdir $workdir; open XML, ">$workdir/content.xml"; print XML <<"CONTENTXML"; <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE office:document-content PUBLIC "-//OpenOffice.org//DTD Offic +eDocument 1.0//EN" "office.dtd"> <office:document-content xmlns:office="http://openoffice.org/2000/offi +ce" xmlns:style="http://openoffice.org/2000/style" xmlns:text="http:/ +/openoffice.org/2000/text" xmlns:table="http://openoffice.org/2000/ta +ble" xmlns:draw="http://openoffice.org/2000/drawing" xmlns:fo="http:/ +/www.w3.org/1999/XSL/Format" xmlns:xlink="http://www.w3.org/1999/xlin +k" xmlns:number="http://openoffice.org/2000/datastyle" xmlns:svg="htt +p://www.w3.org/2000/svg" xmlns:chart="http://openoffice.org/2000/char +t" xmlns:dr3d="http://openoffice.org/2000/dr3d" xmlns:math="http://ww +w.w3.org/1998/Math/MathML" xmlns:form="http://openoffice.org/2000/for +m" xmlns:script="http://openoffice.org/2000/script" office:class="tex +t" office:version="1.0"> <office:script/> <office:font-decls> <style:font-decl style:name="Georgia" fo:font-family="Georgia"/> <style:font-decl style:name="Helmet" fo:font-family="Helmet"/> <style:font-decl style:name="Verdana" fo:font-family="Verdana"/> <style:font-decl style:name="Arial Unicode MS" fo:font-family="&apos +;Arial Unicode MS'" style:font-pitch="variable"/> <style:font-decl style:name="Verdana1" fo:font-family="Verdana" styl +e:font-pitch="variable"/> </office:font-decls> <office:automatic-styles> <style:style style:name="Table1" style:family="table" style:master-p +age-name=""> <style:properties style:width="7.3inch" style:page-number="0" table +:align="margins" style:may-break-between-rows="false"/> </style:style> <style:style style:name="Table1.A" style:family="table-column"> <style:properties style:column-width="0.3319inch" style:rel-column- +width="2978*"/> </style:style> <style:style style:name="Table1.A1" style:family="table-cell"> <style:properties fo:padding="0.0382inch" fo:border-left="0.0007inc +h solid #000000" fo:border-right="none" fo:border-top="0.0007inch sol +id #000000" fo:border-bottom="0.0007inch solid #000000"/> </style:style> <style:style style:name="Table1.V1" style:family="table-cell"> <style:properties fo:padding="0.0382inch" fo:border="0.0007inch sol +id #000000"/> </style:style> <style:style style:name="Table1.A2" style:family="table-cell"> <style:properties fo:padding="0.0382inch" fo:border-left="0.0007inc +h solid #000000" fo:border-right="none" fo:border-top="none" fo:borde +r-bottom="0.0007inch solid #000000"/> </style:style> <style:style style:name="Table1.V2" style:family="table-cell"> <style:properties fo:padding="0.0382inch" fo:border-left="0.0007inc +h solid #000000" fo:border-right="0.0007inch solid #000000" fo:border +-top="none" fo:border-bottom="0.0007inch solid #000000"/> </style:style> <style:style style:name="wordlist" style:family="table"> <style:properties style:width="7.3inch" table:align="margins" style +:may-break-between-rows="false"/> </style:style> <style:style style:name="wordlist.A" style:family="table-column"> <style:properties style:column-width="2.4333inch" style:rel-column- +width="21845*"/> </style:style> <style:style style:name="Table2" style:family="table" style:master-p +age-name=""> <style:properties style:width="7.3inch" style:page-number="0" table +:align="margins" style:may-break-between-rows="false"/> </style:style> <style:style style:name="Table2.A" style:family="table-column"> <style:properties style:column-width="0.3319inch" style:rel-column- +width="2978*"/> </style:style> <style:style style:name="Table2.A1" style:family="table-cell"> <style:properties fo:padding="0.0382inch" fo:border-left="0.0007inc +h solid #000000" fo:border-right="none" fo:border-top="0.0007inch sol +id #000000" fo:border-bottom="0.0007inch solid #000000"/> </style:style> <style:style style:name="Table2.V1" style:family="table-cell"> <style:properties fo:padding="0.0382inch" fo:border="0.0007inch sol +id #000000"/> </style:style> <style:style style:name="Table2.A2" style:family="table-cell"> <style:properties fo:padding="0.0382inch" fo:border-left="0.0007inc +h solid #000000" fo:border-right="none" fo:border-top="none" fo:borde +r-bottom="0.0007inch solid #000000"/> </style:style> <style:style style:name="Table2.V2" style:family="table-cell"> <style:properties fo:padding="0.0382inch" fo:border-left="0.0007inc +h solid #000000" fo:border-right="0.0007inch solid #000000" fo:border +-top="none" fo:border-bottom="0.0007inch solid #000000"/> </style:style> <style:style style:name="P1" style:family="paragraph" style:parent-s +tyle-name="Standard" style:master-page-name=""> <style:properties style:page-number="0" fo:keep-with-next="true"/> </style:style> <style:style style:name="P2" style:family="paragraph" style:parent-s +tyle-name="Table Contents"> <style:properties style:font-name="Verdana1" fo:font-size="12pt" fo +:text-align="center" style:justify-single-word="false"/> </style:style> <style:style style:name="P3" style:family="paragraph" style:parent-s +tyle-name="Standard"> <style:properties fo:keep-with-next="false"/> </style:style> </office:automatic-styles> <office:body> <text:sequence-decls> <text:sequence-decl text:display-outline-level="0" text:name="Illus +tration"/> <text:sequence-decl text:display-outline-level="0" text:name="Table +"/> <text:sequence-decl text:display-outline-level="0" text:name="Text" +/> <text:sequence-decl text:display-outline-level="0" text:name="Drawi +ng"/> </text:sequence-decls> CONTENTXML ; print XML " <text:p text:style-name=\"Standard\"/> ".table('Solution','Table2', \@solutiongrid)." ".table('Puzzle', 'Table1', \@grid)." <text:p text:style-name=\"Standard\"/> <text:p text:style-name=\"Standard\"/> $xmlwordlist <text:p text:style-name=\"Standard\"/> <text:p text:style-name=\"Standard\"/> ".#table('','Table3', [[" ", "A"],["B", "C"]]). # I had some bit of XML wrong, causing the last table to have its co +ntents reduced to only the first cell. # This blank table was a workaround until I figured out what I did w +rong. " <text:p text:style-name=\"P3\"/> </office:body> </office:document-content>\n"; writefiles(); # Great, so now let's zip 'er up: use Archive::Zip qw(:ERROR_CODES :CONSTANTS); my $zipfile = "wordsearch-$title.sxw"; open ZIPFILE, ">$zipfile"; my $zip = Archive::Zip->new(); foreach my $memberName ('content.xml', #'layout-cache', 'META-INF', 'meta.xml', 'settings.xml', 'style +s.xml') { chdir "$workdir"; # This may be unnecessary, depending on how you se +t things up. if (-e $memberName) { warn "Good: member does in fact exist: $memberName\n" if ($debug> +1); if (-d $memberName ) { warn "Member is directory: $memberName\n" if ($debug>1); warn "Can't add tree $memberName\n" if ($zip->addTree( $memberName, $memberName ) != AZ_OK); } else { warn "Member must be file (is not dir): $memberName\n" if ($debu +g>1); $zip->addFile( $memberName ) or warn "Can't add file $memberName\n"; } } else { warn "Member does not exist: $memberName\n"; } } my $status = $zip->writeToFileHandle(*ZIPFILE); close ZIPFILE; print "Wrote $zipfile (status: $status)\n"; exit 0; # Subroutines follow... sub table { my ($title, $table, $grid) = @_; my $rval =<<"TABLETABLE"; <text:p text:style-name="P1">$title:</text:p> <table:table table:name="$table" table:style-name="$table"> <table:table-column table:style-name="$table.A" table:number-column +s-repeated="$gridx"/> TABLETABLE ; $rval .= tablebody($table, $grid)." </table:table>"; return $rval; } sub tablebody { my ($table, $grid) = @_; my @grid = @$grid; my $xml = ""; for $y (1..$gridy) { $xml .= " <table:table-row>\n"; for $x (1..$gridx) { $xml .= " <table:table-cell table:style-name=\"$table.A1\" ta +ble:value-type=\"string\">\n"; if ($grid[$x][$y]) { $xml .= " <text:p text:style-name=\"P2\">$grid[$x][$y]</te +xt:p>\n"; } else { $xml .= " <text:p text:style-name=\"P2\"/>\n"; } $xml .= " </table:table-cell>\n"; } $xml .= " </table:table-row>\n"; } return $xml; } sub place { my ($word, $posn) = @_; my ($x,$y,$o) = @$posn; my ($xd,$yd) = @$o; my @c = split//, $word; # Test everything before doing anything... for $i (1..(length $word)) { my $xp = $x + ($xd * ($i - 1)); my $yp = $y + ($yd * ($i - 1)); if (($xp <= 0) or ($yp <= 0) or ($xp > $gridx) or ($yp > $gridy)) +{ # Won't fit in this position, because it goes off the grid. return undef; } if (($grid[$xp][$yp]) and ($grid[$xp][$yp] ne $c[$i-1])) { # Won't fit in this position due to collision. return undef; } } # If we didn't go off the grid or collide, it must fit. Place it. for $i (1..(length $word)) { my $xp = $x + ($xd * ($i - 1)); my $yp = $y + ($yd * ($i - 1)); $grid[$xp][$yp] = $c[$i-1]; } return $word if $word; return "$word but true"; } sub randomorder { return map { $$_[0] } sort { $$a[1] <=> $$b[1] } map { [$_, rand(17931)] } @_; } sub wordsfromdictionary { my ($numofwords) = @_; open DICT, "<$dictfile"; my @dict = map {chomp;lc $_} <DICT>; close DICT; return map { $dict[rand @dict] } 1..$numofwords; } sub getwords { # A GUI frontend could be substituted here... my $choice = menu('What words should be used for the wordsearch?', [[dict=>'Use random words from a dictionary'], [user=>'Let me type in some words to use'], ]); if ($choice eq 'dict') { print "How many words should be taken from the dictionary?\n"; my ($w) = <STDIN> =~ /(\d+)/; while (not ($w>0)) { print " Please enter a number of words to use in the dictionary. + ==> "; ($w) = <STDIN> =~ /(\d+)/; } return wordsfromdictionary($w); } elsif ($choice eq 'user') { print "Please type one word per line. When you are finished typin +g words, enter a blank line.\n"; my $w = 1; my @w; while ($w) { ($w) = <STDIN> =~ /(\w+)/; push @w, $w if $w; } return @w; } else { die "Whoah, I got confused. My menu subroutine didn't return the +kind of value I expected. This is certainly a bug in my program code. Get Nathan.\n\n" +; } } sub menu { $|++; my ($question, $c) = @_; my @choice = @$c; my $response = 0; while (($response<1) or ($response>@choice)) { print "\n\n\t$question\n"; my $choicenum=0; print ((join$/,map{sprintf"\t% 2d. $$_[1]",++$choicenum} @choice) +."\n\nEnter choice number: ==> "); ($response) = <STDIN> =~ /(\d+)/; if ($debug) { print "You said: $response\n"; if ($response<1) { print "Response less than 1.\n"; } if ($response>@choice) { print "Response greater than ".@choice. +".\n"; } } } my $choice = $choice[$response-1]; # Perl arrays are zero-indexed; I + started my choice numbers at 1, for added end-user comfort. return $$choice[0]; } sub writefiles { # Writes the other files (besides content.xml) needed for the SXW do +cument: open XML, ">$workdir/meta.xml"; print XML <<'METAXML'; <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE office:document-meta PUBLIC "-//OpenOffice.org//DTD OfficeDo +cument 1.0//EN" "office.dtd"> <office:document-meta xmlns:office="http://openoffice.org/2000/office" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org +/dc/elements/1.1/" xmlns:meta="http://openoffice.org/2000/meta" offic +e:version="1.0"> <office:meta> <meta:generator>Original base document created using OpenOffice.org +1.0.2 (Linux); data interpolated by Perl script. </meta:generator><!--SRC641_[7663]_LINUX_INTEL__stronkje_at_1/28/03_ +6:22:06--> <meta:creation-date>2003-09-22T13:30:15</meta:creation-date> <dc:date>2003-09-22T15:21:13</dc:date> <meta:print-date>2003-09-22T14:13:40</meta:print-date> <dc:language>en-US</dc:language> <meta:editing-cycles>3</meta:editing-cycles> <meta:editing-duration>PT1H51M13S</meta:editing-duration> <meta:user-defined meta:name="Info 1"/> <meta:user-defined meta:name="Info 2"/> <meta:user-defined meta:name="Info 3"/> <meta:user-defined meta:name="Info 4"/> <meta:document-statistic meta:table-count="3" meta:image-count="0" m +eta:object-count="0" meta:page-count="2" meta:paragraph-count="985" m +eta:word-count="42" meta:character-count="130"/> </office:meta> </office:document-meta> METAXML ;close XML; open XML, ">$workdir/settings.xml"; print XML <<'SETTINGSXML'; <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE office:document-settings PUBLIC "-//OpenOffice.org//DTD Offi +ceDocument 1.0//EN" "office.dtd"> <office:document-settings xmlns:office="http://openoffice.org/2000/off +ice" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:config="http:// +openoffice.org/2001/config" office:version="1.0"> <office:settings> <config:config-item-set config:name="view-settings"> <config:config-item config:name="ViewAreaTop" config:type="int">117 +63</config:config-item> <config:config-item config:name="ViewAreaLeft" config:type="int">0< +/config:config-item> <config:config-item config:name="ViewAreaWidth" config:type="int">2 +3661</config:config-item> <config:config-item config:name="ViewAreaHeight" config:type="int"> +13951</config:config-item> <config:config-item config:name="ShowRedlineChanges" config:type="b +oolean">true</config:config-item> <config:config-item config:name="ShowHeaderWhileBrowsing" config:ty +pe="boolean">false</config:config-item> <config:config-item config:name="ShowFooterWhileBrowsing" config:ty +pe="boolean">false</config:config-item> <config:config-item config:name="InBrowseMode" config:type="boolean +">false</config:config-item> <config:config-item-map-indexed config:name="Views"> <config:config-item-map-entry> <config:config-item config:name="ViewId" config:type="string">vie +w2</config:config-item> <config:config-item config:name="ViewLeft" config:type="int">1488 +7</config:config-item> <config:config-item config:name="ViewTop" config:type="int">21059 +</config:config-item> <config:config-item config:name="VisibleLeft" config:type="int">0 +</config:config-item> <config:config-item config:name="VisibleTop" config:type="int">11 +763</config:config-item> <config:config-item config:name="VisibleRight" config:type="int"> +23659</config:config-item> <config:config-item config:name="VisibleBottom" config:type="int" +>25712</config:config-item> <config:config-item config:name="ZoomType" config:type="short">3< +/config:config-item> <config:config-item config:name="ZoomFactor" config:type="short"> +103</config:config-item> <config:config-item config:name="IsSelectedFrame" config:type="bo +olean">false</config:config-item> </config:config-item-map-entry> </config:config-item-map-indexed> </config:config-item-set> <config:config-item-set config:name="configuration-settings"> <config:config-item config:name="AddParaTableSpacing" config:type=" +boolean">false</config:config-item> <config:config-item config:name="PrintReversed" config:type="boolea +n">false</config:config-item> <config:config-item config:name="LinkUpdateMode" config:type="short +">1</config:config-item> <config:config-item config:name="CharacterCompressionType" config:t +ype="short">0</config:config-item> <config:config-item config:name="PrintSingleJobs" config:type="bool +ean">false</config:config-item> <config:config-item config:name="PrintPaperFromSetup" config:type=" +boolean">false</config:config-item> <config:config-item config:name="PrintLeftPages" config:type="boole +an">true</config:config-item> <config:config-item config:name="PrintTables" config:type="boolean" +>true</config:config-item> <config:config-item config:name="ChartAutoUpdate" config:type="bool +ean">true</config:config-item> <config:config-item config:name="PrintControls" config:type="boolea +n">true</config:config-item> <config:config-item config:name="PrinterSetup" config:type="base64B +inary">ugL+/zxBZmljaW8+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU0dFTlBSVAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAWAAMAAAIAAAAA//8BAAhSAAAEdAAASm9iRGF0YSAxCnByaW50Z +XI9PEFmaWNpbz4Kb3JpZW50YXRpb249UG9ydHJhaXQKY29waWVzPTEKc2NhbGU9MAptYX +JnaW5kYWp1c3RtZW50PTAsMCwwLDAKY29sb3JkZXB0aD0yNApwc2xldmVsPTAKY29sb3J +kZXZpY2U9MApQUERDb250ZXhEYXRhClBhZ2VTaXplOkE0AABlcgAANAAAIDA6IGRyaXZl +ciBzZXR0aW5nLCAxOiBsZXZlbCAxLCAyOiBsZXZlbDIA8AYAAFgAAAD4BgAAWHcoRcDQU +0AYdyhFAQAAAEAAAAAYAAAAEHcoRRB3KEU7ICAgICAgICAgaWYga2V5IGlzIGFic2VudC +B0aGUgZGVmYXVsdCBpcyAwAG4AQQA4AAAAUAcAAJB3KEXA0FNAcHcoRQEAQwAgAAAAGAA +AAGh3KEVodyhFOyBQU0xldmVsPTAAIGtleRgAAACIBwAAqHcoRcDQU0DA0FNAAXQgdngA +AACgBwAAIHgoRcDQU0DAdyhFAWljAGAAAAAYAAAAuHcoRbh3KEU7IFBQRF9QYWdlU2l6Z +TogdGhlIGRlZmF1bHQgcGFnZSBzaXplIHRvIHVzZS4gSWYgYSBzcGVjaWZpYyBwcmludG +VyIGRvZXMAAAAAEAgAABgAAAAYCAAAOHgoRcDQU0A=</config:config-item> <config:config-item config:name="PrintAnnotationMode" config:type=" +short">0</config:config-item> <config:config-item config:name="ApplyUserData" config:type="boolea +n">true</config:config-item> <config:config-item config:name="FieldAutoUpdate" config:type="bool +ean">true</config:config-item> <config:config-item config:name="SaveVersionOnClose" config:type="b +oolean">false</config:config-item> <config:config-item config:name="SaveGlobalDocumentLinks" config:ty +pe="boolean">false</config:config-item> <config:config-item config:name="IsKernAsianPunctuation" config:typ +e="boolean">false</config:config-item> <config:config-item config:name="AlignTabStopPosition" config:type= +"boolean">false</config:config-item> <config:config-item config:name="CurrentDatabaseDataSource" config: +type="string"/> <config:config-item config:name="PrinterName" config:type="string"> +<Aficio></config:config-item> <config:config-item config:name="PrintFaxName" config:type="string" +/> <config:config-item config:name="PrintRightPages" config:type="bool +ean">true</config:config-item> <config:config-item config:name="AddParaTableSpacingAtStart" config +:type="boolean">false</config:config-item> <config:config-item config:name="PrintProspect" config:type="boolea +n">false</config:config-item> <config:config-item config:name="PrintGraphics" config:type="boolea +n">true</config:config-item> <config:config-item config:name="CurrentDatabaseCommandType" config +:type="int">0</config:config-item> <config:config-item config:name="PrintPageBackground" config:type=" +boolean">true</config:config-item> <config:config-item config:name="CurrentDatabaseCommand" config:typ +e="string"/> <config:config-item config:name="PrintDrawings" config:type="boolea +n">true</config:config-item> <config:config-item config:name="PrintBlackFonts" config:type="bool +ean">false</config:config-item> </config:config-item-set> </office:settings> </office:document-settings> SETTINGSXML ;close XML; open XML, ">$workdir/styles.xml"; print XML <<'STYLESXML'; <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE office:document-styles PUBLIC "-//OpenOffice.org//DTD Office +Document 1.0//EN" "office.dtd"> <office:document-styles xmlns:office="http://openoffice.org/2000/offic +e" xmlns:style="http://openoffice.org/2000/style" xmlns:text="http:// +openoffice.org/2000/text" xmlns:table="http://openoffice.org/2000/tab +le" xmlns:draw="http://openoffice.org/2000/drawing" xmlns:fo="http:// +www.w3.org/1999/XSL/Format" xmlns:xlink="http://www.w3.org/1999/xlink +" xmlns:number="http://openoffice.org/2000/datastyle" xmlns:svg="http +://www.w3.org/2000/svg" xmlns:chart="http://openoffice.org/2000/chart +" xmlns:dr3d="http://openoffice.org/2000/dr3d" xmlns:math="http://www +.w3.org/1998/Math/MathML" xmlns:form="http://openoffice.org/2000/form +" xmlns:script="http://openoffice.org/2000/script" office:version="1. +0"> <office:font-decls> <style:font-decl style:name="Georgia" fo:font-family="Georgia"/> <style:font-decl style:name="Helmet" fo:font-family="Helmet"/> <style:font-decl style:name="Verdana" fo:font-family="Verdana"/> <style:font-decl style:name="Arial Unicode MS" fo:font-family="&apos +;Arial Unicode MS'" style:font-pitch="variable"/> <style:font-decl style:name="Verdana1" fo:font-family="Verdana" styl +e:font-pitch="variable"/> </office:font-decls> <office:styles> <style:default-style style:family="graphics"> <style:properties draw:start-line-spacing-horizontal="0.1114inch" d +raw:start-line-spacing-vertical="0.1114inch" draw:end-line-spacing-ho +rizontal="0.1114inch" draw:end-line-spacing-vertical="0.1114inch" fo: +color="#000000" style:font-name="Verdana" fo:font-size="12pt" fo:lang +uage="en" fo:country="US" style:font-name-asian="Helmet" style:font-s +ize-asian="12pt" style:language-asian="none" style:country-asian="non +e" style:font-name-complex="Arial Unicode MS" style:font-size-complex +="12pt" style:language-complex="none" style:country-complex="none" st +yle:text-autospace="ideograph-alpha" style:punctuation-wrap="simple" +style:line-break="strict"> <style:tab-stops/> </style:properties> </style:default-style> <style:default-style style:family="paragraph"> <style:properties fo:color="#000000" style:font-name="Verdana" fo:f +ont-size="12pt" fo:language="en" fo:country="US" style:font-name-asia +n="Helmet" style:font-size-asian="12pt" style:language-asian="none" s +tyle:country-asian="none" style:font-name-complex="Arial Unicode MS" +style:font-size-complex="12pt" style:language-complex="none" style:co +untry-complex="none" fo:hyphenate="false" fo:hyphenation-remain-char- +count="2" fo:hyphenation-push-char-count="2" fo:hyphenation-ladder-co +unt="no-limit" style:text-autospace="ideograph-alpha" style:punctuati +on-wrap="hanging" style:line-break="strict" style:tab-stop-distance=" +0.5inch" style:writing-mode="lr-tb"/> </style:default-style> <style:style style:name="Standard" style:family="paragraph" style:cl +ass="text"/> <style:style style:name="Text body" style:family="paragraph" style:p +arent-style-name="Standard" style:class="text"> <style:properties fo:margin-top="0inch" fo:margin-bottom="0.0835inc +h"/> </style:style> <style:style style:name="Heading" style:family="paragraph" style:par +ent-style-name="Standard" style:next-style-name="Text body" style:cla +ss="text"> <style:properties fo:margin-top="0.1665inch" fo:margin-bottom="0.08 +35inch" style:font-name="Georgia" fo:font-size="14pt" style:font-name +-asian="Helmet" style:font-size-asian="14pt" style:font-name-complex= +"Arial Unicode MS" style:font-size-complex="14pt" fo:keep-with-next=" +true"/> </style:style> <style:style style:name="List" style:family="paragraph" style:parent +-style-name="Text body" style:class="list"> <style:properties style:font-name="Verdana" style:font-name-asian=" +Helmet"/> </style:style> <style:style style:name="Table Contents" style:family="paragraph" st +yle:parent-style-name="Text body" style:class="extra"> <style:properties text:number-lines="false" text:line-number="0"/> </style:style> <style:style style:name="Caption" style:family="paragraph" style:par +ent-style-name="Standard" style:class="extra"> <style:properties fo:margin-top="0.0835inch" fo:margin-bottom="0.08 +35inch" style:font-name="Verdana" fo:font-size="10pt" fo:font-style=" +italic" style:font-name-asian="Helmet" style:font-size-asian="10pt" s +tyle:font-style-asian="italic" style:font-size-complex="10pt" style:f +ont-style-complex="italic" text:number-lines="false" text:line-number +="0"/> </style:style> <style:style style:name="Index" style:family="paragraph" style:paren +t-style-name="Standard" style:class="index"> <style:properties style:font-name="Verdana" style:font-name-asian=" +Helmet" text:number-lines="false" text:line-number="0"/> </style:style> <text:outline-style> <text:outline-level-style text:level="1" style:num-format=""/> <text:outline-level-style text:level="2" style:num-format=""/> <text:outline-level-style text:level="3" style:num-format=""/> <text:outline-level-style text:level="4" style:num-format=""/> <text:outline-level-style text:level="5" style:num-format=""/> <text:outline-level-style text:level="6" style:num-format=""/> <text:outline-level-style text:level="7" style:num-format=""/> <text:outline-level-style text:level="8" style:num-format=""/> <text:outline-level-style text:level="9" style:num-format=""/> <text:outline-level-style text:level="10" style:num-format=""/> </text:outline-style> <text:footnotes-configuration style:num-format="1" text:start-value= +"0" text:footnotes-position="page" text:start-numbering-at="document" +/> <text:endnotes-configuration style:num-format="i" text:start-value=" +0"/> <text:linenumbering-configuration text:number-lines="false" text:off +set="0.1965inch" style:num-format="1" text:number-position="left" tex +t:increment="5"/> </office:styles> <office:automatic-styles> <style:page-master style:name="pm1"> <style:properties fo:page-width="8.5inch" fo:page-height="11inch" s +tyle:num-format="1" style:print-orientation="portrait" fo:margin-top= +"0.6inch" fo:margin-bottom="0.6inch" fo:margin-left="0.6inch" fo:marg +in-right="0.6inch" style:footnote-max-height="0inch"> <style:footnote-sep style:width="0.0071inch" style:distance-before +-sep="0.0398inch" style:distance-after-sep="0.0398inch" style:adjustm +ent="left" style:rel-width="25%" style:color="#000000"/> </style:properties> <style:header-style/> <style:footer-style/> </style:page-master> </office:automatic-styles> <office:master-styles> <style:master-page style:name="Standard" style:page-master-name="pm1 +"/> </office:master-styles> </office:document-styles> STYLESXML ;close XML; mkdir "$workdir/META-INF"; open XML, ">$workdir/META-INF/manifest.xml"; print XML <<'MANIFEST'; <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1. +0//EN" "Manifest.dtd"> <manifest:manifest xmlns:manifest="http://openoffice.org/2001/manifest +"> <manifest:file-entry manifest:media-type="application/vnd.sun.xml.wri +ter" manifest:full-path="/"/> <manifest:file-entry manifest:media-type="" manifest:full-path="Pictu +res/"/> <manifest:file-entry manifest:media-type="appication/binary" manifest +:full-path="layout-cache"/> <manifest:file-entry manifest:media-type="text/xml" manifest:full-pat +h="content.xml"/> <manifest:file-entry manifest:media-type="text/xml" manifest:full-pat +h="styles.xml"/> <manifest:file-entry manifest:media-type="text/xml" manifest:full-pat +h="meta.xml"/> <manifest:file-entry manifest:media-type="text/xml" manifest:full-pat +h="settings.xml"/> </manifest:manifest> MANIFEST ;close XML; } # word-search-maker was written 2003 September for Galion Public # Library and is primarily intended for use within the library; this # code is not thoroughly tested and is only distributed with the # expectation that anyone who wants to use it will test and evaluate # it first to determine whether it will meet their needs, what bugs # need to be fixed, what improvements that need to be made, et cetera # and will make any necessary adjustments before distributing or using # it. Galion Public Library can make no warrantee that it is complete # (since it is not, in fact, complete) nor that it is suitable for any # purpose other than our own use. Anyone who wishes to distribute, # copy, modify, or use this code or any derivative works thereof may # ONLY do so with the understanding and under the agreement that # Galion Public Library is not and can not be responsible in any way # for any resulting occurrances that may ensue; you the distributor or # the end use must assume full responsibility for any distribution or # use of this work; otherwise you are expressly forbidden to # distribute, copy, modify, or use it, to the greatest extent that # such actions can be forbidden under copyright laws applicable in the # US and other countries.
This code is preliminary, short on features, not totally portable, and probably long on bugs; it's alpha, and needs work. But I have been able to get it to work here, enough to be adequate for our small needs, and so I will probably not do much more with it without a pressing reason; therefore, I'm posting it here in case anyone else wants to fix it up a bit more and make it into something worth distributing. Anyone who wants to take responsibility for maintaining this may select a suitable open-source license (BSD, Artistic, or whatever). My name, and that of the library, do not need to remain associated with it (and should not if substantial changes are made).
$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
|
|---|