usps_flags 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,545 @@
1
+ # Controller class for generating files.
2
+ class USPSFlags::Generate
3
+ # The primary controller method. Generates an SVG file or SVG data.
4
+ #
5
+ # @param [String] flag The flag type to generate.
6
+ # @param [String] outfile The path to save the SVG file to. If not set, prints to console.
7
+ # @param [Boolean] field Whether to generate the flag field (including any border).
8
+ # @param [String] scale The image scale divisor factor.
9
+ # @return [String] Returns the SVG data.
10
+ def self.get(flag, outfile: nil, scale: nil, field: true)
11
+ flag = flag.upcase
12
+ if ["CRUISE", "OIC"].include?(flag)
13
+ self.pennant(type: flag, outfile: outfile, scale: scale)
14
+ elsif flag.upcase == "ENSIGN"
15
+ self.ensign(outfile: outfile, scale: scale)
16
+ elsif flag.upcase == "US"
17
+ self.us(outfile: outfile, scale: scale)
18
+ elsif flag.upcase == "WHEEL"
19
+ self.wheel(outfile: outfile, scale: scale)
20
+ else
21
+ self.flag(rank: flag, outfile: outfile, scale: scale, field: field)
22
+ end
23
+ end
24
+
25
+ # Convert SVG data into a PNG file.
26
+ #
27
+ # @param [String] svg The SVG data.
28
+ # @param [String] outfile The path to save the PNG file to. (File is not accessible if this is left blank.)
29
+ # @param [Boolean] trim Whether to trim the generated PNG file of excess transparency.
30
+ def self.png(svg, outfile: nil, trim: false)
31
+ outfile = "temp.png" if outfile.nil?
32
+ temp_svg = ::File.new("temp.svg", "w+")
33
+ temp_svg.write(svg)
34
+ temp_svg.flush
35
+
36
+ MiniMagick::Tool::Convert.new do |convert|
37
+ convert << "-background" << "none"
38
+ convert << "-format" << "png"
39
+ convert << "-trim" if trim
40
+ convert << temp_svg.path
41
+ convert << outfile
42
+ end
43
+ ensure
44
+ ::File.delete(temp_svg) if ::File.exists?(temp_svg)
45
+ ::File.delete("temp.png") if ::File.exists?("temp.png")
46
+ end
47
+
48
+ # Generate all static SVG and PNG files, and automaticall generates zip archives for download.
49
+ #
50
+ # @param [Boolean] svg Whether to generate SVG images.
51
+ # @param [Boolean] png Whether to generate PNG images.
52
+ # @param [Boolean] zips Whether to create zip archives for all images created. Does not create a zip for skipped formats.
53
+ # @param [Boolean] reset Whether to delete all previous files before generating new files.
54
+ def self.all(svg: true, png: true, zips: true, reset: true)
55
+ if reset
56
+ ::FileUtils.rm_rf(Dir.glob("#{USPSFlags::Config.flags_dir}/SVG/*"))
57
+ ::FileUtils.rm_rf(Dir.glob("#{USPSFlags::Config.flags_dir}/PNG/*"))
58
+ ::FileUtils.rm_rf(Dir.glob("#{USPSFlags::Config.flags_dir}/ZIP/*"))
59
+ ::FileUtils.mkdir_p("#{USPSFlags::Config.flags_dir}/SVG/insignia")
60
+ ::FileUtils.mkdir_p("#{USPSFlags::Config.flags_dir}/PNG/insignia")
61
+ puts "Cleared previous files."
62
+ end
63
+
64
+ flags = USPSFlags::Helpers.valid_flags(:all)
65
+ insignia_flags = USPSFlags::Helpers.valid_flags(:insignia)
66
+
67
+ max_length = flags.map(&:length).max
68
+ puts "\nSVGs generate a single file.",
69
+ "PNGs generate full-res, 1500w, 1000w, 500w, and thumbnail files.",
70
+ "Corresponding rank insignia (including smaller sizes) are also generated, as appropriate."
71
+ USPSFlags::Helpers.log "\n#{Time.now.strftime('%Y%m%d.%H%M%S%z')} – Generating static files...\n\n"
72
+ USPSFlags::Helpers.log "Flag | SVG | PNG | Run time\n".rjust(max_length+31),
73
+ "\n".rjust(max_length+32, "-")
74
+
75
+ overall_start_time = Time.now
76
+
77
+ flags.each do |flag|
78
+ start_time = Time.now
79
+ USPSFlags::Helpers.log " | | _ _ _ _ _ | \r".rjust(max_length+31, " ")
80
+ flag = flag.upcase
81
+ USPSFlags::Helpers.log "#{flag.rjust(max_length)} |"
82
+
83
+ svg_file = "#{USPSFlags::Config.flags_dir}/SVG/#{flag}.svg"
84
+ png_file = "#{USPSFlags::Config.flags_dir}/PNG/#{flag}.png"
85
+
86
+ svg_ins_file = "#{USPSFlags::Config.flags_dir}/SVG/insignia/#{flag}.svg"
87
+ png_ins_file = "#{USPSFlags::Config.flags_dir}/PNG/insignia/#{flag}.png"
88
+
89
+ past = (flag[0] == "P" && flag != "PORTCAP")
90
+
91
+ if svg
92
+ begin
93
+ USPSFlags::Helpers.log " "
94
+ self.get flag, outfile: svg_file, scale: 1
95
+ USPSFlags::Helpers.log "S"
96
+ if past || !insignia_flags.include?(flag)
97
+ USPSFlags::Helpers.log "-"
98
+ else
99
+ self.get flag, field: false, outfile: svg_ins_file, scale: 1
100
+ USPSFlags::Helpers.log "I"
101
+ end
102
+ rescue => e
103
+ USPSFlags::Helpers.log "x -> #{e.message}"
104
+ end
105
+ else
106
+ USPSFlags::Helpers.log "-"
107
+ end
108
+
109
+ if png
110
+ USPSFlags::Helpers.log " | "
111
+ begin
112
+ USPSFlags::Helpers.log "…\b"
113
+ USPSFlags::Generate.png(File.read(svg_file), outfile: png_file) unless ::File.exists?(png_file)
114
+ USPSFlags::Helpers.log "F"
115
+ if past || !insignia_flags.include?(flag)
116
+ USPSFlags::Helpers.log "-"
117
+ else
118
+ USPSFlags::Helpers.log "…\b"
119
+ USPSFlags::Generate.png(File.read(svg_ins_file), outfile: png_ins_file, trim: true) unless ::File.exists?(png_ins_file)
120
+ USPSFlags::Helpers.log "I"
121
+ end
122
+ sizes = {1500 => "H", 1000 => "K", 500 => "D", "thumb" => "T"}
123
+ sizes.keys.each do |size|
124
+ if ::File.exists?("#{USPSFlags::Config.flags_dir}/PNG/#{flag}.#{size}.png")
125
+ USPSFlags::Helpers.log "."
126
+ else
127
+ USPSFlags::Helpers.log "…\b"
128
+ if size == "thumb"
129
+ size_key = size
130
+ size = case flag
131
+ when "ENSIGN"
132
+ 200
133
+ when "US"
134
+ 300
135
+ else
136
+ 150
137
+ end
138
+ else
139
+ size_key = size
140
+ end
141
+ MiniMagick::Tool::Convert.new do |convert|
142
+ convert << "-background" << "none"
143
+ convert << "-format" << "png"
144
+ convert << "-resize" << "#{size}"
145
+ convert << png_file
146
+ convert << "#{USPSFlags::Config.flags_dir}/PNG/#{flag}.#{size_key}.png"
147
+ end
148
+ USPSFlags::Helpers.log sizes[size_key]
149
+
150
+ if ::File.exists?(png_ins_file)
151
+ if ::File.exists?("#{USPSFlags::Config.flags_dir}/PNG/insignia/#{flag}.#{size}.png")
152
+ USPSFlags::Helpers.log "."
153
+ elsif MiniMagick::Image.open(png_ins_file)[:width] > size
154
+ USPSFlags::Helpers.log "…\b"
155
+ MiniMagick::Tool::Convert.new do |convert|
156
+ convert << "-background" << "none"
157
+ convert << "-format" << "png"
158
+ convert << "-resize" << "#{size}"
159
+ convert << png_ins_file
160
+ convert << "#{USPSFlags::Config.flags_dir}/PNG/insignia/#{flag}.#{size_key}.png"
161
+ end
162
+ USPSFlags::Helpers.log "i"
163
+ else
164
+ USPSFlags::Helpers.log "+"
165
+ end
166
+ else
167
+ USPSFlags::Helpers.log "-"
168
+ end
169
+ end
170
+ end
171
+ rescue => e
172
+ USPSFlags::Helpers.log "x -> #{e.message}"
173
+ end
174
+ else
175
+ USPSFlags::Helpers.log "- "
176
+ end
177
+ run_time = (Time.now - start_time).round(4).to_s[(0..5)].ljust(6, "0")
178
+ USPSFlags::Helpers.log " | #{run_time} s\n"
179
+ end
180
+
181
+ self.zips(svg: svg, png: png) if zips
182
+
183
+ USPSFlags::Helpers.log "\nTotal run time: #{Time.now - overall_start_time} s\n\n"
184
+
185
+ nil
186
+ end
187
+
188
+ # Generate zip archives of current static image files.
189
+ #
190
+ # @param [Boolean] svg Generate zip archive of SVG images.
191
+ # @param [Boolean] png Generate zip archive of PNG images.
192
+ def self.zips(svg: true, png: true)
193
+ ["svg", "png"].each do |format|
194
+ if eval(format)
195
+ zip = "#{USPSFlags::Config.flags_dir}/ZIP/USPS_Flags.#{format}.zip"
196
+ ::File.delete(zip) if ::File.exists?(zip)
197
+ Zip::File.open(zip, Zip::File::CREATE) do |z|
198
+ Dir.glob("#{USPSFlags::Config.flags_dir}/#{format.upcase}/**/*").each do |f|
199
+ if f.split("/").last(2).first == "insignia"
200
+ filename = "insignia/#{f.split("/").last}"
201
+ z.add(filename, f)
202
+ else
203
+ z.add(f.split("/").last, f)
204
+ end
205
+ end
206
+ end
207
+ puts "Generated #{format.upcase} Zip"
208
+ end
209
+ end
210
+ end
211
+
212
+ # Generate trident spec sheet as an SVG image.
213
+ #
214
+ # @param [String] outfile The path to save the SVG file to. If not set, prints to console.
215
+ # @param [Integer] fly The nominal fly length of an appropriate flag field for the generated tridents. Size labels scale to this size.
216
+ # @param [String] outfile The unit to append to all trident measurements.
217
+ # @param [String] scale The image scale divisor factor.
218
+ # @return [String] Returns the SVG data.
219
+ def self.spec(outfile: nil, fly: nil, unit: nil, scale: nil)
220
+ fly = fly.nil? ? USPSFlags::Config::BASE_FLY : fly
221
+ final_svg = ""
222
+ final_svg << USPSFlags::Core.headers(scale: scale)
223
+ final_svg << USPSFlags::Core.trident_spec(fly: fly, unit: unit)
224
+ final_svg << USPSFlags::Core.footer
225
+
226
+ if outfile.nil?
227
+ puts final_svg, "\n"
228
+ else
229
+ f = ::File.new(outfile, "w+")
230
+ f.write(final_svg)
231
+ f.close
232
+ end
233
+ final_svg
234
+ end
235
+
236
+ private
237
+ def self.flag(rank: nil, width: USPSFlags::Config::BASE_FLY, outfile: nil, scale: nil, field: true)
238
+ raise "Error: No rank specified." if rank.nil?
239
+ final_svg = ""
240
+
241
+ final_svg << USPSFlags::Core.headers(scale: scale)
242
+
243
+ rank.slice!(0) if !field && rank[0].upcase == "P" && rank != "PORTCAP"
244
+ rank = "CDR" if rank == "C"
245
+
246
+ case rank.upcase
247
+ when "PLTC"
248
+ style = :past
249
+ color = :red
250
+ type = :s
251
+ count = 2
252
+ when "PC"
253
+ style = :past
254
+ color = :blue
255
+ type = :s
256
+ count = 3
257
+ when "PORTCAP"
258
+ style = :swallowtail
259
+ color = :white
260
+ type = :pc
261
+ count = 1
262
+ when "FLEETCAP"
263
+ style = :swallowtail
264
+ color = :white
265
+ type = :fc
266
+ count = 1
267
+ when "LT"
268
+ style = :swallowtail
269
+ color = :white
270
+ type = :s
271
+ count = 1
272
+ when "FLT"
273
+ style = :swallowtail
274
+ color = :white
275
+ type = :f
276
+ count = 1
277
+ when "1LT"
278
+ style = :regular
279
+ color = :white
280
+ type = :s
281
+ count = 1
282
+ when "LTC"
283
+ style = :regular
284
+ color = :red
285
+ type = :s
286
+ count = 2
287
+ when "CDR"
288
+ style = :regular
289
+ color = :blue
290
+ type = :s
291
+ count = 3
292
+
293
+ when "PDLTC"
294
+ style = :past
295
+ color = :red
296
+ type = :d
297
+ count = 2
298
+ when "PDC"
299
+ style = :past
300
+ color = :blue
301
+ type = :d
302
+ count = 3
303
+ when "DLT"
304
+ style = :swallowtail
305
+ color = :white
306
+ type = :d
307
+ count = 1
308
+ when "DAIDE"
309
+ style = :swallowtail
310
+ color = :white
311
+ type = :a
312
+ count = 1
313
+ when "DFLT"
314
+ style = :swallowtail
315
+ color = :white
316
+ type = :f
317
+ count = 2
318
+ when "D1LT"
319
+ style = :regular
320
+ color = :white
321
+ type = :d
322
+ count = 1
323
+ when "DLTC"
324
+ style = :regular
325
+ color = :red
326
+ type = :d
327
+ count = 2
328
+ when "DC"
329
+ style = :regular
330
+ color = :blue
331
+ type = :d
332
+ count = 3
333
+
334
+ when "PSTFC"
335
+ style = :past
336
+ color = :white
337
+ type = :stf
338
+ count = 1
339
+ when "PRC"
340
+ style = :past
341
+ color = :white
342
+ type = :n
343
+ count = 1
344
+ when "PVC"
345
+ style = :past
346
+ color = :red
347
+ type = :n
348
+ count = 2
349
+ when "PCC"
350
+ style = :past
351
+ color = :blue
352
+ type = :n
353
+ count = 3
354
+ when "NAIDE"
355
+ style = :swallowtail
356
+ color = :white
357
+ type = :a
358
+ count = 2
359
+ when "NFLT"
360
+ style = :swallowtail
361
+ color = :white
362
+ type = :f
363
+ count = 3
364
+ when "STFC"
365
+ style = :regular
366
+ color = :white
367
+ type = :stf
368
+ count = 1
369
+ when "RC"
370
+ style = :regular
371
+ color = :white
372
+ type = :n
373
+ count = 1
374
+ when "VC"
375
+ style = :regular
376
+ color = :red
377
+ type = :n
378
+ count = 2
379
+ when "CC"
380
+ style = :regular
381
+ color = :blue
382
+ type = :n
383
+ count = 3
384
+ end
385
+
386
+ final_svg << USPSFlags::Core.field(style: style, color: color) if field
387
+
388
+ final_svg << "<g transform=\"translate(-150, 400)\"><g transform=\"scale(0.58333)\">" if style == :past
389
+
390
+ if type == :n && count == 3
391
+ # The side C/C tridents are angled 45 degrees, and intersect the central one at 1/3 up from the bottom
392
+ trident = USPSFlags::Core.trident(type, color: (field ? :white : :blue))
393
+ x_distance = USPSFlags::Config::BASE_FLY*4/39
394
+ y_distance = USPSFlags::Config::BASE_FLY*5/78
395
+ final_svg << "<g transform=\"translate(-#{x_distance}, #{y_distance})\"><g transform=\"rotate(-45, #{USPSFlags::Config::BASE_FLY/2}, #{USPSFlags::Config::BASE_HOIST/2})\">\n#{trident}</g></g>"
396
+ final_svg << "\n#{trident}"
397
+ final_svg << "<g transform=\"translate(#{x_distance}, #{y_distance})\"><g transform=\"rotate(45, #{USPSFlags::Config::BASE_FLY/2}, #{USPSFlags::Config::BASE_HOIST/2})\">\n#{trident}</g></g>"
398
+ elsif type == :n && count == 2
399
+ # V/C tridents are angled 45 degrees, and intersect at 15/32 up from the bottom
400
+ trident = USPSFlags::Core.trident(type, color: (field ? :white : :red))
401
+ x_distance = USPSFlags::Config::BASE_FLY*4/55
402
+ final_svg << "<g transform=\"translate(-#{x_distance})\"><g transform=\"rotate(-45, #{USPSFlags::Config::BASE_FLY/2}, #{USPSFlags::Config::BASE_HOIST/2})\">\n#{trident}</g></g>"
403
+ final_svg << "<g transform=\"translate(#{x_distance})\"><g transform=\"rotate(45, #{USPSFlags::Config::BASE_FLY/2}, #{USPSFlags::Config::BASE_HOIST/2})\">\n#{trident}</g></g>"
404
+ elsif [:s, :d].include?(type) && count == 3
405
+ # Cdr and D/C tridents are spaced 1/2 the fly apart with the central one 1/4 the fly above the sides
406
+ trident = USPSFlags::Core.trident(type, color: (field ? :white : :blue), field_color: color)
407
+ x_distance = USPSFlags::Config::BASE_FLY/4
408
+ y_distance = USPSFlags::Config::BASE_FLY/16
409
+ final_svg << "<g transform=\"translate(-#{x_distance}, #{y_distance})\">\n#{trident}</g>"
410
+ final_svg << "<g transform=\"translate(0, -#{y_distance+1})\">\n#{trident}</g>"
411
+ final_svg << "<g transform=\"translate(#{x_distance}, #{y_distance})\">\n#{trident}</g>"
412
+ elsif [:s, :d].include?(type) && count == 2
413
+ # Lt/C and D/Lt/C tridents are spaced 1/3 the fly apart
414
+ trident = USPSFlags::Core.trident(type, color: (field ? :white : :red), field_color: color)
415
+ x_distance = USPSFlags::Config::BASE_FLY/6
416
+ final_svg << "<g transform=\"translate(-#{x_distance})\">\n#{trident}</g>"
417
+ final_svg << "<g transform=\"translate(#{x_distance})\">\n#{trident}</g>"
418
+ elsif [:s, :d, :stf, :n].include?(type)
419
+ if %w[LT DLT].include?(rank.upcase)
420
+ # Swallowtail tridents need to move towards the hoist due to the tails
421
+ x_distance = USPSFlags::Config::BASE_FLY/10 if field
422
+ final_svg << "<g transform=\"translate(-#{x_distance})\">" if field
423
+ final_svg << USPSFlags::Core.trident(type, field_color: color, color: :red)
424
+ final_svg << "</g>" if field
425
+ else
426
+ # All other tridents are centered on the field
427
+ final_svg << USPSFlags::Core.trident(type, field_color: color)
428
+ end
429
+ else
430
+ # Special Flags
431
+ # Paths were designed for a base fly of 3000 pixels, but the base was changed for more useful fractions.
432
+ final_svg << "<g transform=\"translate(#{USPSFlags::Config::BASE_FLY/10})\">" unless field
433
+ final_svg << "<g transform=\"scale(#{Rational(USPSFlags::Config::BASE_FLY/3000).to_f})\">"
434
+ case type
435
+ when :a
436
+ level = case count
437
+ when 1
438
+ :d
439
+ when 2
440
+ :n
441
+ end
442
+ final_svg << USPSFlags::Core.binoculars(level)
443
+ when :f
444
+ level = case count
445
+ when 1
446
+ :s
447
+ when 2
448
+ :d
449
+ when 3
450
+ :n
451
+ end
452
+ final_svg << USPSFlags::Core.trumpet(level)
453
+ when :fc
454
+ final_svg << USPSFlags::Core.anchor
455
+ when :pc
456
+ final_svg << USPSFlags::Core.lighthouse
457
+ end
458
+ final_svg << "</g>"
459
+ final_svg << "</g>" unless field
460
+ end
461
+
462
+ final_svg << "</g></g>" if style == :past
463
+
464
+ final_svg << USPSFlags::Core.footer
465
+
466
+ if outfile.nil?
467
+ puts final_svg, "\n"
468
+ elsif outfile != ""
469
+ f = ::File.new(outfile, "w+")
470
+ f.write(final_svg)
471
+ f.close
472
+ end
473
+ final_svg
474
+ end
475
+
476
+ def self.pennant(type: "cruise", outfile: nil, scale: nil)
477
+ final_svg = ""
478
+ final_svg << USPSFlags::Core.headers(pennant: true, scale: scale)
479
+ final_svg << USPSFlags::Core.pennant(type)
480
+ final_svg << USPSFlags::Core.footer
481
+
482
+ if outfile.nil?
483
+ puts final_svg, "\n"
484
+ elsif outfile != ""
485
+ f = ::File.new(outfile, "w+")
486
+ f.write(final_svg)
487
+ f.close
488
+ end
489
+ final_svg
490
+ end
491
+
492
+ def self.ensign(outfile: nil, scale: nil)
493
+ final_svg = ""
494
+ final_svg << USPSFlags::Core.headers(scale: scale)
495
+ final_svg << USPSFlags::Core.ensign
496
+ final_svg << USPSFlags::Core.footer
497
+
498
+ if outfile.nil?
499
+ puts final_svg, "\n"
500
+ elsif outfile != ""
501
+ f = ::File.new(outfile, "w+")
502
+ f.write(final_svg)
503
+ f.close
504
+ end
505
+ final_svg
506
+ end
507
+
508
+ def self.wheel(outfile: nil, scale: nil)
509
+ width = 4327.4667
510
+ height = 4286.9333
511
+ final_svg = ""
512
+ final_svg << USPSFlags::Core.headers(width: width, height: height, scale: scale)
513
+ final_svg << USPSFlags::Core.wheel
514
+ final_svg << USPSFlags::Core.footer
515
+
516
+ if outfile.nil?
517
+ puts final_svg, "\n"
518
+ elsif outfile != ""
519
+ f = ::File.new(outfile, "w+")
520
+ f.write(final_svg)
521
+ f.close
522
+ end
523
+ final_svg
524
+ end
525
+
526
+ def self.us(outfile: nil, scale: nil)
527
+ base_hoist = 2000.to_f
528
+ base_fly = base_hoist * 1.91
529
+ hoist = scale.nil? ? base_hoist : (base_hoist / scale)
530
+ fly = hoist * 1.91
531
+ final_svg = ""
532
+ final_svg << USPSFlags::Core.headers(width: fly, height: hoist, scale: scale)
533
+ final_svg << USPSFlags::Core.us
534
+ final_svg << USPSFlags::Core.footer
535
+
536
+ if outfile.nil?
537
+ puts final_svg, "\n"
538
+ elsif outfile != ""
539
+ f = ::File.new(outfile, "w+")
540
+ f.write(final_svg)
541
+ f.close
542
+ end
543
+ final_svg
544
+ end
545
+ end