picolena 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/History.txt +14 -0
  2. data/Manifest.txt +28 -8
  3. data/config/files_to_clean +1 -0
  4. data/config/requirements.rb +1 -1
  5. data/lib/picolena/config/basic.rb +2 -1
  6. data/lib/picolena/config/icons_and_filetypes.yml +5 -0
  7. data/lib/picolena/picolena_generator.rb +3 -1
  8. data/lib/picolena/templates/app/helpers/documents_helper.rb +4 -4
  9. data/lib/picolena/templates/app/models/document.rb +27 -4
  10. data/lib/picolena/templates/app/models/indexer.rb +6 -2
  11. data/lib/picolena/templates/app/models/plain_text_extractor.rb +27 -13
  12. data/lib/picolena/templates/app/models/query.rb +2 -2
  13. data/lib/picolena/templates/app/views/documents/_document.html.haml +1 -1
  14. data/lib/picolena/templates/config/environments/development.rb +2 -0
  15. data/lib/picolena/templates/config/initializers/001_load_ferret.rb +17 -0
  16. data/lib/picolena/templates/config/initializers/{001_load_custom_config.rb → 002_load_custom_config.rb} +1 -2
  17. data/lib/picolena/templates/config/initializers/{002_load_indexed_dirs.rb → 003_load_indexed_dirs.rb} +0 -0
  18. data/lib/picolena/templates/config/initializers/{003_load_white_list_IPs.rb → 004_load_white_list_IPs.rb} +0 -0
  19. data/lib/picolena/templates/config/initializers/{004_load_plain_text_extractors.rb → 005_load_plain_text_extractors.rb} +1 -1
  20. data/lib/picolena/templates/config/initializers/{005_load_custom_title_and_names_and_links.rb → 006_load_custom_title_and_names_and_links.rb} +0 -0
  21. data/lib/picolena/templates/config/initializers/{006_load_icons.rb → 007_load_icons.rb} +0 -0
  22. data/lib/picolena/templates/config/initializers/{007_load_performance_tweaks.rb → 008_load_performance_tweaks.rb} +0 -0
  23. data/lib/picolena/templates/lib/core_exts.rb +52 -0
  24. data/lib/picolena/templates/lib/development_helpers.rb +35 -0
  25. data/lib/picolena/templates/lib/plain_text_extractor_dsl.rb +128 -0
  26. data/lib/picolena/templates/lib/plain_text_extractors/adobe.pdf.rb +2 -2
  27. data/lib/picolena/templates/lib/plain_text_extractors/adobe.photoshop.rb +12 -0
  28. data/lib/picolena/templates/lib/plain_text_extractors/html.rb +1 -1
  29. data/lib/picolena/templates/lib/plain_text_extractors/ms.excel.rb +4 -4
  30. data/lib/picolena/templates/lib/plain_text_extractors/ms.powerpoint.rb +4 -4
  31. data/lib/picolena/templates/lib/plain_text_extractors/ms.rtf.rb +3 -3
  32. data/lib/picolena/templates/lib/plain_text_extractors/ms.word.rb +4 -4
  33. data/lib/picolena/templates/lib/plain_text_extractors/opendocument.presentation.rb +2 -2
  34. data/lib/picolena/templates/lib/plain_text_extractors/opendocument.spreadsheet.rb +2 -2
  35. data/lib/picolena/templates/lib/plain_text_extractors/opendocument.text.rb +2 -2
  36. data/lib/picolena/templates/lib/plain_text_extractors/pictures.rb +15 -4
  37. data/lib/picolena/templates/lib/plain_text_extractors/plain_text.rb +9 -2
  38. data/lib/picolena/templates/lib/plain_text_extractors/rar.rb +18 -0
  39. data/lib/picolena/templates/lib/plain_text_extractors/videos.rb +13 -0
  40. data/lib/picolena/templates/lib/plain_text_extractors/zip.rb +17 -0
  41. data/lib/picolena/templates/lib/tasks/extract.rake +16 -0
  42. data/lib/picolena/templates/lib/tasks/install_dependencies.rake +1 -1
  43. data/lib/picolena/templates/public/images/thumbnails/NOTE +2 -0
  44. data/lib/picolena/templates/spec/controllers/documents_controller_spec.rb +8 -0
  45. data/lib/picolena/templates/spec/helpers/documents_helper_spec.rb +12 -1
  46. data/lib/picolena/templates/spec/models/basic_finder_spec.rb +6 -4
  47. data/lib/picolena/templates/spec/models/document_spec.rb +24 -4
  48. data/lib/picolena/templates/spec/models/finder_spec.rb +18 -11
  49. data/lib/picolena/templates/spec/models/host_indexing_system_spec.rb +1 -1
  50. data/lib/picolena/templates/spec/models/plain_text_extractor_spec.rb +25 -8
  51. data/lib/picolena/templates/spec/models/query_spec.rb +4 -5
  52. data/lib/picolena/templates/spec/spec_helper.rb +9 -0
  53. data/lib/picolena/templates/spec/test_dirs/indexed/archives/dumb_file.rar +0 -0
  54. data/lib/picolena/templates/spec/test_dirs/indexed/archives/some_test_files.zip +0 -0
  55. data/lib/picolena/templates/spec/test_dirs/indexed/basic/fake_thumbnailer +14 -0
  56. data/lib/picolena/templates/spec/test_dirs/indexed/media/badminton.avi +0 -0
  57. data/lib/picolena/templates/spec/test_dirs/indexed/media/caution.tif +0 -0
  58. data/lib/picolena/templates/spec/test_dirs/indexed/media/cygnus.jpeg +0 -0
  59. data/lib/picolena/templates/spec/test_dirs/indexed/media/diceface.eps +79 -0
  60. data/lib/picolena/templates/spec/test_dirs/indexed/media/glass.png +0 -0
  61. data/lib/picolena/templates/spec/test_dirs/indexed/media/gnu.bmp +0 -0
  62. data/lib/picolena/templates/spec/test_dirs/indexed/media/picolena.psd +0 -0
  63. data/lib/picolena/templates/spec/test_dirs/indexed/media/rails_logo_remix.gif +0 -0
  64. data/lib/picolena/templates/spec/test_dirs/indexed/media/warning.tiff +0 -0
  65. data/lib/picolena/version.rb +1 -1
  66. data/website/index.html +1 -1
  67. metadata +31 -32
  68. data.tar.gz.sig +0 -0
  69. data/lib/picolena/templates/lib/plain_text_extractor_DSL.rb +0 -88
  70. metadata.gz.sig +0 -0
@@ -0,0 +1,13 @@
1
+ # Basic support for different videos
2
+ # It only has been tested with .avi so far
3
+
4
+ PlainTextExtractor.new {
5
+ every :avi, :mpg, :mpeg, :mov, :wmv
6
+ as "video/*"
7
+ aka "some video"
8
+
9
+ extract_thumbnail_with 'ffmpegthumbnailer -i SOURCE -o THUMBNAIL'
10
+
11
+ extract_content_with 'exiftool SOURCE'
12
+ which_should_for_example_extract 'Image Size 320x200 Duration 1.96s', :from => 'badminton.avi'
13
+ }
@@ -0,0 +1,17 @@
1
+ PlainTextExtractor.new {
2
+ every :zip
3
+ as "archive/zip"
4
+ aka "ZIP Archive"
5
+
6
+ # TODO: Transpose this structure to support .tgz, .rar, .deb, 7z, ar, .cab, .ace, .lzma, .bz2
7
+ # NOTE: What to do when the archive is too big?
8
+
9
+ extract_content_from_archive_with "unzip SOURCE -d TEMPDIR"
10
+
11
+ which_should_for_example_extract 'some_test_files some_dir dumb.rb', :from => 'some_test_files.zip'
12
+ or_extract 'puts 2+2', :from => 'some_test_files.zip'
13
+ # Now *that* is some cool spec!
14
+ # It relies on .jpg and .rar Extractors
15
+ or_extract '"This sentence is hidden in the EXIF metadata of a .jpg file archived in a .rar file archived in a .zip file!"',
16
+ :from => 'some_test_files.zip'
17
+ }
@@ -0,0 +1,16 @@
1
+ desc 'Extract information from given file'
2
+ namespace :extract do
3
+ desc 'Extract plain text content from given file'
4
+ task :content => :environment do
5
+ ARGV.shift
6
+ ARGV.select{|fn| File.file?(fn)}.each{|filename|
7
+ begin
8
+ content=PlainTextExtractor.extract_content_from(filename)
9
+ puts "### Content extracted from : #{filename}"
10
+ puts content
11
+ rescue => e
12
+ puts "### No content extracted from #{filename} (#{e.message})"
13
+ end
14
+ }
15
+ end
16
+ end
@@ -30,7 +30,7 @@ namespace :install_dependencies do
30
30
  task :deb_packages do
31
31
  root_privileges_required!
32
32
  #TODO: Should load this list from defined PlainTextExtractor's
33
- packages=%w{antiword poppler-utils odt2txt html2text catdoc unrtf mguesser libdbm-ruby1.8}.join(" ")
33
+ packages=%w{antiword poppler-utils odt2txt html2text catdoc unrtf mguesser libdbm-ruby1.8 libimage-exiftool-perl ffmpegthumbnailer imagemagick}.join(" ")
34
34
  puts "Installing "<<packages
35
35
  system("apt-get install "<<packages)
36
36
  end
@@ -0,0 +1,2 @@
1
+ ## TODO: Find a way to manage thumbnails for different environments.
2
+ Accessorily, leaving this file here prevents rake manifest:refresh to remove thumbnails dir
@@ -91,6 +91,14 @@ describe DocumentsController do
91
91
  orig_assigns['matching_documents'].any?{|doc| doc.matching_content.join.starts_with?("just another content test in a pdf file")}.should be_true
92
92
  end
93
93
 
94
+ it "GET 'show' should accept * as query" do
95
+ params_from(:get, '/documents/*').should == {:controller => 'documents', :action => 'show', :id => '*'}
96
+ lambda{get 'show', :id=>'*'}.should_not raise_error(ActionController::RoutingError)
97
+ response.should be_success
98
+ orig_assigns['matching_documents'].entries.should_not be_empty
99
+ orig_assigns['total_hits'].should == Indexer.size
100
+ end
101
+
94
102
  it "GET 'show' should accept combined queries" do
95
103
  get 'show', :id=>'test filetype:pdf'
96
104
  response.should be_success
@@ -3,7 +3,18 @@ require File.dirname(__FILE__) + '/../spec_helper'
3
3
  describe DocumentsHelper do
4
4
  PlainTextExtractor.supported_extensions.each{|ext|
5
5
  it "should have an icon for .#{ext} filetype" do
6
- helper.icon_for(ext).should_not be_nil
6
+ Picolena::FiletypeToIconSymbol[ext].should_not be_nil
7
7
  end
8
8
  }
9
+
10
+ it "should not raise for filetypes without any associated icon when calling icon_for" do
11
+ Picolena::FiletypeToIconSymbol[:xyz].should be_nil
12
+ document=Spec::Mocks::Mock.new('Document', :ext_as_sym => :xyz, :icon_path => nil)
13
+ lambda {helper.icon_for(document)}.should_not raise_error
14
+ end
15
+
16
+ it "should not raise for files without content when calling highlighted_cache" do
17
+ document=Spec::Mocks::Mock.new('Document', :highlighted_cache => "\f")
18
+ lambda {helper.highlighted_cache(document, 'some_query')}.should_not raise_error
19
+ end
9
20
  end
@@ -17,7 +17,8 @@ describe "Finder without index on disk" do
17
17
  end
18
18
 
19
19
  it "should create index" do
20
- Picolena::IndexedDirectories.replace({'spec/test_dirs/indexed/just_one_doc'=>'//justonedoc/'})
20
+ new_dir=File.expand_path(File.join(RAILS_ROOT, 'spec/test_dirs/indexed/just_one_doc'))
21
+ Picolena::IndexedDirectories.replace(new_dir => '//justonedoc/')
21
22
  lambda {@finder_with_new_index=Finder.new("test moi")}.should change(Indexer, :index_exists?).from(false).to(true)
22
23
  File.exists?(File.join(@new_index_path,'_0.cfs')).should be_true
23
24
  Indexer.index.size.should >0
@@ -33,12 +34,13 @@ describe "Finder without index on disk" do
33
34
  Picolena::IndexedDirectories.replace(@original_indexed_dirs)
34
35
  Picolena::IndexSavePath.replace(@original_index_path)
35
36
  Picolena::MetaIndexPath.replace(File.join(@original_index_path,'meta'))
37
+ FileUtils.remove_entry_secure(@new_index_path)
36
38
  end
37
39
  end
38
40
 
39
41
 
40
42
  fields={
41
- # description => key
43
+ # description => key
42
44
  :content => :content,
43
45
  :complete_path => :complete_path,
44
46
  :basename => :basename,
@@ -95,9 +97,9 @@ describe "Basic Finder" do
95
97
 
96
98
  it "should know how much time was needed for execution" do
97
99
  finder=Finder.new("yet another stupid query")
98
- finder.executed?.should be_nil
100
+ finder.should_not be_executed
99
101
  finder.time_needed.should > 0
100
- finder.executed?.should be_true
102
+ finder.should be_executed
101
103
  end
102
104
 
103
105
  it "should not need more than 100ms to find documents" do
@@ -47,8 +47,6 @@ describe Document do
47
47
  another_doc.content.should == "just a content test\nin a txt file"
48
48
  end
49
49
 
50
- #FIXME: Check if content has been cached before trying to display cached content. extension check is not enough
51
- #(e.g. unreadable pdf file)
52
50
  it "should know its cached content" do
53
51
  another_doc=Document.new("spec/test_dirs/indexed/basic/plain.txt")
54
52
  another_doc.cached.should == "just a content test\nin a txt file"
@@ -104,8 +102,6 @@ describe Document do
104
102
  it "should not be considered supported if binary" do
105
103
  Document.new("spec/test_dirs/indexed/others/BIN_FILE_WITHOUT_EXTENSION").should_not be_supported
106
104
  end
107
-
108
-
109
105
 
110
106
  it "should know its language when enough content is available" do
111
107
  Document.new("spec/test_dirs/indexed/lang/goethe").language.should == "de"
@@ -133,6 +129,30 @@ describe Document do
133
129
  @valid_document.matching_content.should include("thermal cooling")
134
130
  end
135
131
 
132
+ it "should know its icon_path if a thumbnail if available" do
133
+ doc=Document.new("spec/test_dirs/indexed/media/badminton.avi")
134
+ doc.icon_path.should_not be_nil
135
+ doc.icon_path.should == "thumbnails/#{doc.probably_unique_id}.jpg"
136
+ end
137
+
138
+ it "should know its icon_path if an icon if available for its mimetype" do
139
+ doc=Document.new("spec/test_dirs/indexed/others/xor.vee")
140
+ doc.icon_path.should_not be_nil
141
+ doc.icon_path.should == 'icons/insel.png'
142
+ end
143
+
144
+ it "should return nil as icon_path if no icon or thumbnail is available" do
145
+ doc=Document.new("spec/test_dirs/indexed/others/ghjopdfg.xyz")
146
+ doc.icon_path.should be_nil
147
+ end
148
+
149
+ it "should not raise when asked for highlighted_cache even though content is empty" do
150
+ doc=Document.new("spec/test_dirs/indexed/others/nested/unreadable.pdf")
151
+ doc.should_not have_content
152
+ lambda {doc.highlighted_cache('unreadable')}.should_not raise_error
153
+ doc.highlighted_cache('unreadable').should ~ /^\s*$/
154
+ end
155
+
136
156
  after(:all) do
137
157
  revert_changes!("spec/test_dirs/indexed/others/placeholder.txt","Absorption and Adsorption cooling machines!!!")
138
158
  end
@@ -48,6 +48,13 @@ describe Finder do
48
48
  Finder.new("crossed").total_hits.should >= 2
49
49
  end
50
50
 
51
+ it "should find documents according to parts of their alias_path" do
52
+ media_files=Dir["#{RAILS_ROOT}/spec/test_dirs/indexed/media/*"]
53
+ Finder.new("media").total_hits.should >= media_files.size
54
+ Finder.new("nested indexed").matching_documents.collect{|d| d.filename}.should include("ReadMe.rtf")
55
+ matching_document_for("test_dirs yet_another_dir ext:xlsx").basename.should == "office2007-excel"
56
+ end
57
+
51
58
  it "should also index unreadable files with known mimetypes" do
52
59
  Finder.new("unreadable.pdf").matching_documents.should_not be_empty
53
60
  Finder.new("too_small.doc").matching_documents.should_not be_empty
@@ -81,18 +88,18 @@ describe Finder do
81
88
  matching_document_for("bäñüßé").content.should == "just to know if files are indexed with utf8 filenames"
82
89
  end
83
90
 
84
- it "should find documents according to their modification date" do
85
- matching_document_for("19831209").basename.should == "office2003-word-template"
86
- matching_document_for("19820216").basename.should == "basic"
87
- end
91
+ it "should find documents according to their modification date"
92
+ ## Not implemented yet!
93
+ # matching_document_for("19831209").basename.should == "office2003-word-template"
94
+ # matching_document_for("19820216").basename.should == "basic"
88
95
 
89
- it "should find documents according to their modification year" do
90
- Finder.new("date:<1982").matching_documents.should be_empty
91
- matching_document_for("date:<1983").filename.should == "basic.pdf"
92
- matching_document_for("date:1982").filename.should == "basic.pdf"
93
- matching_document_for("year:1983").filename.should == "basic.pdf"
94
- matching_document_for("date:>=1989 AND date:<=1992").filename.should == "placeholder.txt"
95
- end
96
+ it "should find documents according to their modification year"
97
+ ## Not implemented yet!
98
+ # Finder.new("date:<1982").matching_documents.should be_empty
99
+ # matching_document_for("date:<1983").filename.should == "basic.pdf"
100
+ # matching_document_for("date:1982").filename.should == "basic.pdf"
101
+ # matching_document_for("year:1983").filename.should == "basic.pdf"
102
+ # matching_document_for("date:>=1989 AND date:<=1992").filename.should == "placeholder.txt"
96
103
 
97
104
  it "should not concatenate cells from xls file" do
98
105
  Finder.new("content:ABC").matching_documents.select{|doc| doc.extname==".xls"}.should be_empty
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/../spec_helper'
3
3
  describe "Host indexing system" do
4
4
  PlainTextExtractor.dependencies.each do |dependency|
5
5
  it "should have #{dependency} installed" do
6
- IO.popen("which #{dependency}"){|i| i.read.should_not be_empty}
6
+ dependency.should be_installed
7
7
  end
8
8
  end
9
9
 
@@ -7,10 +7,16 @@ describe "PlainTextExtractors" do
7
7
 
8
8
  PlainTextExtractor.all.each{|extractor|
9
9
  extractor.exts.each{|ext|
10
- should_extract= "should be able to extract content from #{extractor.description} (.#{ext})"
10
+ should_extract_content = "should be able to extract content from #{extractor.description} (.#{ext})"
11
+ should_extract_thumbnail= "should be able to extract thumbnail from #{extractor.description} (.#{ext})"
11
12
  content_and_file_examples_for_this_ext=extractor.content_and_file_examples.select{|content,file| File.ext_as_sym(file)==ext}
12
- unless content_and_file_examples_for_this_ext.empty? then
13
- it should_extract do
13
+ if content_and_file_examples_for_this_ext.empty? then
14
+ ## It means that the spec for this extension file is "Not yet implemented"!
15
+ ## add this line to the corresponding extractor in lib/extractors:
16
+ # which_should_for_example_extract 'some content', :from => 'a file you could add in spec/test_dirs/indexed/'
17
+ it should_extract_content
18
+ else
19
+ it should_extract_content do
14
20
  content_and_file_examples_for_this_ext.each{|content_example,file_example|
15
21
  finder=Finder.new(content_example)
16
22
  finder.execute!
@@ -19,11 +25,22 @@ describe "PlainTextExtractors" do
19
25
  matching_documents_filenames.should include(file_example)
20
26
  }
21
27
  end
22
- else
23
- ## It means that the spec for this extension file is "Not yet implemented"!
24
- ## add this line to the corresponding extractor in lib/extractors:
25
- # which_should_for_example_extract 'some content', :from => 'a file you could add in spec/test_dirs/indexed/'
26
- it should_extract
28
+ end
29
+
30
+ if extractor.thumbnail_command then
31
+ doc=Document.find_by_extension(ext)
32
+ if doc then
33
+ it should_extract_thumbnail do
34
+ thumb_path=doc.send(:thumbnail_path)
35
+ # NOTE: This doesn't seem to work, why?
36
+ # File.should exist(doc.send(:thumbnail_path))
37
+ File.exist?(thumb_path).should be_true
38
+ # NOTE: It seems that ffmpegthumbnailer outputs a png file even with -o output.jpg
39
+ %x(file -ib #{thumb_path}).chomp.should =~ /image\/(jpeg|png)/
40
+ end
41
+ else
42
+ it should_extract_thumbnail
43
+ end
27
44
  end
28
45
  }
29
46
  }
@@ -87,10 +87,9 @@ describe Query do
87
87
  Query.content_terms("LIKE absorption").include?("adsorption").should be_true
88
88
  end
89
89
 
90
- it "should remove / and - from date queries" do
91
- #TODO: Implement corresponding analyzer
92
- Query.extract_from("date:1982-02-16").should == Query.extract_from("date:19820216")
93
- Query.extract_from("date:1982/02/16").should == Query.extract_from("date:19820216")
94
- end
90
+ it "should remove / and - from date queries"
91
+ ##TODO: Implement corresponding analyzer
92
+ # Query.extract_from("date:1982-02-16").should == Query.extract_from("date:19820216")
93
+ # Query.extract_from("date:1982/02/16").should == Query.extract_from("date:19820216")
95
94
 
96
95
  end
@@ -11,6 +11,15 @@ def revert_changes!(file,content)
11
11
  }
12
12
  end
13
13
 
14
+
15
+ class Document
16
+ def self.find_by_extension(ext)
17
+ Finder.new("ext:#{ext}").matching_documents.first
18
+ end
19
+ end
20
+
21
+
22
+ ## Rspec-rails assume that everybody uses ActiveRecord, but that's not Picolena's case.
14
23
  module Spec
15
24
  module Matchers
16
25
  class Change
@@ -0,0 +1,14 @@
1
+ #! /bin/bash
2
+ #
3
+ # extract_thumbnail_with 'spec/test_dirs/indexed/basic/fake_thumbnailer SOURCE THUMBNAIL'
4
+ # written in a PlainTextExtractpr will write some debug information
5
+ # about the thumbnailing process
6
+ echo "Beginning" >> $2
7
+ echo $1 >> $2
8
+ date >> $2
9
+ for ((i=1; i<=3; i+=1)); do
10
+ sleep 1
11
+ echo $i >> $2
12
+ done
13
+ date >> $2
14
+ echo "End" >> $2
@@ -0,0 +1,79 @@
1
+ %!PS-Adobe-2.0 EPSF-1.2
2
+ %%BoundingBox: 0 0 258 43
3
+ %
4
+ % commented version of EPSDICE.EPS by Thomas A. Heim
5
+ %
6
+ % LICENSE: LPPL
7
+ %
8
+ % 2001/02/09 -- thomas.heim@unibas.ch
9
+ %
10
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
+ %
12
+ % face measures 32x32 and fits within a 43x43 bounding box
13
+ %
14
+ % ==> if you change these dimensions, you will have to adjust
15
+ % the bounding box of the clipped region in the .STY file!
16
+ %
17
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
18
+ %
19
+ % the frame macro: a simple box with rounded corners
20
+ %
21
+ % takes one argument off the stack: n (x-offset is 43*(n - 1) )
22
+ %
23
+ /frame {
24
+ 1 sub 43 mul % calculate 43*(n-1), given n on the stack
25
+ /o exch def % store result in o
26
+ gsave
27
+ newpath
28
+ o 0 translate % shift coordinate system by offset o in x
29
+ 32 5 moveto %
30
+ 32 10 5 -90 0 arc % the frame goes from 5 to 37 units both
31
+ 37 32 lineto % in x and y, with rounded corners having
32
+ 32 32 5 0 90 arc % radius 5 units, centered 5 units inward
33
+ 10 37 lineto % in both directions
34
+ 10 32 5 90 180 arc
35
+ 5 10 lineto
36
+ 10 10 5 180 270 arc
37
+ closepath
38
+ stroke
39
+ grestore
40
+ } def
41
+ %
42
+ % dot positions are designated by (x,y) labels from (1,1) to (3,3)
43
+ %
44
+ % change position and radius of filled circles as you like
45
+ %
46
+ % the dot macro: a filled circle
47
+ %
48
+ % takes three arguments off the stack:
49
+ % n (x-offset 43*(n - 1) )
50
+ % x-label: 1, 2, or 3
51
+ % y-label: 1, 2, or 3
52
+ /dot {
53
+ /y exch def % store y-label of dot
54
+ /x exch def % store x-label of dot
55
+ 1 sub 43 mul % calculate 43*(n-1), given n on the stack
56
+ /o exch def % store result in offset o
57
+ gsave
58
+ newpath
59
+ o 0 translate % shift coordinate system by offset o in x
60
+ x 8 mul 5 add % x-position of dot: 8*x+5 (-> 13,21,29)
61
+ y 8 mul 5 add % y-position of dot: 8*y+5 (-> 13,21,29)
62
+ 3.5 0 360 arc % I like big dots (radius 3.5 units)
63
+ closepath
64
+ fill
65
+ grestore
66
+ } def
67
+ %
68
+ 2 setlinewidth % lines 2 units wide
69
+ 0 setgray % fill the dots in black
70
+ %
71
+ % now use the macros to draw the dice faces in loops
72
+ %
73
+ 1 1 6 { frame } for % the six frames
74
+ 2 1 6 { dup 1 2 3 { dup dot } for } for % (1,1), (3,3) on 2, 3, 4, 5, 6
75
+ 1 2 5 { 2 2 dot } for % (2,2) dot on 1, 3, 5
76
+ 4 1 6 { dup 1 3 dot 3 1 dot } for % (1,3), (3,1) dots on 4, 5, 6
77
+ 1 2 3 { 6 exch 2 dot } for % (1,2), (3,2) dots only on 6
78
+ %%EOF
79
+
@@ -2,7 +2,7 @@ module Picolena #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 2
5
- TINY = 0
5
+ TINY = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end