#!ruby -Ku require "vr/vruby" require "vr/vrdialog" require "vr/vrcomctl" require "vr/vrlayout" require "vr/vrddrop" require "vr/vrcontrol" require "vr/vrhandler" require "uconv" require "png" BS_DEFPUSHBUTTON = 0x01 class TextEditDialog < VRModalDialog def construct self.caption = "Text" move 250, 250, 400, 255 addControl(VREditCombobox, "cmbKeyword", "", 5, 12, 200, 300, WStyle::WS_TABSTOP) addControl(VREdit, "translated_keyword", "", 5, 50, 200, 20, WStyle::WS_TABSTOP) addControl(VRStatic, "lbl", "lang:", 220, 52, 40, 20) addControl(VREdit, "language_tag", "", 250, 50, 50, 20, WStyle::WS_TABSTOP) addControl(VRCheckbox, "chkCompress", "&Compress", 310, 52, 80, 15, WStyle::WS_TABSTOP) addControl(VRText, "text", "", 5, 80, 383, 140, 0x502110C4) addControl(VRButton, "btnOK", "&OK", 220, 10, 80, 30, WStyle::WS_TABSTOP) addControl(VRButton, "btnCancel", "&Cancel", 305, 10, 80, 30, WStyle::WS_TABSTOP) addControl(VRCheckbox, "chkInternational", "&International", 30, 30, 80, 20, WStyle::WS_TABSTOP) @font = @screen.factory.newfont("Tahoma", 13) @text.setFont @font @cmbKeyword.setFont @font @btnOK.setFont @font @btnCancel.setFont @font @chkCompress.setFont @font @chkInternational.setFont @font @translated_keyword.setFont @font @language_tag.setFont @font @lbl.setFont @font @chkCompress.check(true) @chkInternational.check(true) @chkInternational.visible = false @cmbKeyword.setListStrings(["Title", "Author", "Description", "Copyright", "Creation Time", "Software", "Disclaimer", "Warning", "Source", "Comment"]) target = @parent.instance_eval {@target} if target @text.text = Uconv.u8tosjis(target[2][:text]).gsub(/\n/, "\r\n") idx = @cmbKeyword.findString(target[2][:keyword]) if idx == -1 @cmbKeyword.addString(target[2][:keyword]) idx = @cmbKeyword.countStrings-1 end @cmbKeyword.select(idx) @chkCompress.check(target[2][:compression_flag] == 1 ? true : false) @language_tag.text = target[2][:language_tag] @translated_keyword.text = Uconv.u8tosjis(target[2][:translated_keyword]) end centering end def btnOK_clicked close({:international => @chkInternational.checked?, :language_tag => @language_tag.text, :translated_keyword => @translated_keyword.text, :compress => @chkCompress.checked?, :text => @text.text, :keyword => @cmbKeyword.text}) end def btnCancel_clicked close(nil) end end class MainListview < VRListview include VRMouseFeasible def vrinit super add_parentcall("rbuttonup") end end class MainForm < VRForm include VRDropFileTarget include VRFullsizeLayoutManager include VRMenuUseable VERSION = "20041112b" def construct self.caption = "PNGText" move(200, 200, 800, 400) addControl(MainListview, "lview", "") [["Chunk", 50], ["Attributes", 160], ["Length", 80], ["CRC", 80], ["Contents", 600]].each do |coldata| @lview.addColumn(*coldata) end @lview.style += WStyle::LVS_SINGLESEL @lview.lvexstyle = WExStyle::LVS_EX_FULLROWSELECT setMenu newMenu.set([ ["&File", [ ["&Open", "mnuFileOpen"], ["&Save", "mnuFileSave", VRMenuItem::GRAYED], ["Save &As...", "mnuFileSaveAs", VRMenuItem::GRAYED], VRMenu::SEPARATOR, ["E&xit", "mnuFileExit"] ] ], ["&Edit", [ ["&Edit Chunk...", "mnuEditEditChunk", VRMenuItem::GRAYED], ["&Delete", "mnuEditDelete", VRMenuItem::GRAYED], VRMenu::SEPARATOR, ["Add Text...", "mnuEditAddText", VRMenuItem::GRAYED], ["&Convert to iTXt...", "mnuEditConvert", VRMenuItem::GRAYED] ] ], ["&Help", [ ["&About", "mnuHelpAbout"] ] ] ]) @popup = newPopupMenu.set([ ["&Edit Chunk...", "mnuPopupEditChunk", VRMenuItem::GRAYED], ["&Delete", "mnuPopupDelete", VRMenuItem::GRAYED], VRMenu::SEPARATOR, ["Add Text..", "mnuPopupAddText", VRMenuItem::GRAYED] ]) end def mnuFileOpen_clicked fn = SWin::CommonDialog.openFilename(self, [ ["PNG File (*.png)","*.png"],["All File (*.*)","*.*"] ]) return unless fn load_png(fn) end def mnuFileSave_clicked File.open(@file, "wb") do |f| f.print @png.dump end @edit = false update_caption end def mnuFileSaveAs_clicked fn = SWin::CommonDialog.saveFilename(self, [ ["PNG File (*.png)","*.png"],["All File (*.*)","*.*"] ], nil, nil, ".png") return unless fn @file = fn File.open(@file, "wb") do |f| f.print @png.dump end @edit = false update_caption end def mnuFileExit_clicked close end def mnuEditEditChunk_clicked @target = @png.data[@lview.focusedItem] dlg = @screen.modalform(self, nil, TextEditDialog) if dlg @png.delete(@lview.focusedItem) add_text(dlg) end end alias :mnuPopupEditChunk_clicked :mnuEditEditChunk_clicked def mnuEditDelete_clicked @png.delete(@lview.focusedItem) refresh @edit = true update_caption end alias :mnuPopupDelete_clicked :mnuEditDelete_clicked def mnuEditAddText_clicked @target = nil dlg = @screen.modalform(self, nil, TextEditDialog) add_text(dlg) end alias :mnuPopupAddText_clicked :mnuEditAddText_clicked def mnuEditConvert_clicked if messageBox("Convert all text chunks tXTt and zTXt to iTXt?", self.caption, 0x20 | 0x01 ) == 1 data = [] @png.data.each_with_index do |c, i| if c[0] == :tEXt || c[0] == :zTXt data << c[2] end end data.each do |i| @png.add_iTXt(i[:keyword], i[:text], "", "", true, 0, 9) end @png.data.reject! {|c| c[0] == :tEXt || c[0] == :zTXt } @edit = true update_caption refresh end end def mnuHelpAbout_clicked messageBox("I18N PNG Text Editor\nversion: #{VERSION}", self.caption, 0x20) end def lview_itemchanged(idx, state) return unless @open case @png.data[@lview.focusedItem][0] #when :tEXt, :zTXt, :iTXt when :iTXt @mnuEditEditChunk.state = 0 @mnuPopupEditChunk.state = 0 @mnuEditDelete.state = 0 @mnuPopupDelete.state = 0 else @mnuEditEditChunk.state = 1 @mnuPopupEditChunk.state = 1 @mnuEditDelete.state = 1 @mnuPopupDelete.state = 1 end end def lview_dblclicked return unless @open case @png.data[@lview.focusedItem][0] when :iTXt @target = @png.data[@lview.focusedItem] dlg = @screen.modalform(self, nil, TextEditDialog) if dlg @png.delete(@lview.focusedItem) add_text(dlg) end end end def lview_rbuttonup(shift, x, y) showPopup(@popup) end def add_text(dlg) if dlg && !dlg[:keyword].empty? && !dlg[:text].empty? if dlg[:keyword] =~ /^[a-zA-Z][a-zA-Z ]*[a-zA-Z]$/ && dlg[:language_tag] =~ /^([a-z][a-z](-[a-z]+)?)?$/ dlg[:text] = Uconv.sjistou8(dlg[:text]).gsub(/\r\n?/, "\n") p dlg[:text] dlg[:translated_keyword] = Uconv.sjistou8(dlg[:translated_keyword]) unless dlg[:translated_keyword].empty? @png.add_iTXt(dlg[:keyword], dlg[:text], dlg[:language_tag], dlg[:translated_keyword], dlg[:compress], 0, 9) refresh else messageBox("Keyword or language_tag includes invalid character", self.caption, 0) end @edit = true update_caption end end def refresh @lview.clearItems @png.data.each do |chunk| schunk = chunk[0].to_s length = chunk[1].length crc = [Zlib.crc32(schunk + chunk[1])].pack("N*").unpack("H*") attributes = "" contents = "" if ((schunk[0] & 0b00100000) == 0) attributes << "critical" else attributes << "ancillary" #attributes << ( ((schunk[1] & 0b00100000) == 0) ? ", public" : ", private" ) attributes << ( ((schunk[3] & 0b00100000) == 0) ? ", unsafe to copy" : ", safe to copy" ) end case chunk[0] when :IHDR contents = "PNG image header: #{@png.width}x#{@png.height}, #{@png.bit_depth}bits/sample, #{@png.interlace_method == 1 ? '' : 'no'}interlaced" when :PLTE contents = "pallet, #{chunk[2].length} entries" when :bKGD contents = "background color = (#{chunk[2][:red]}, #{chunk[2][:green]}, #{chunk[2][:blue]})" when :sRGB contents = "sRGB color space" when :sBIT contents = "siginificant bits/sample: #{chunk[2][:gray] ? 'G:'+chunk[2][:gray].to_s : ''} #{chunk[2][:red] ? 'R:'+chunk[2][:red].to_s : ''} #{chunk[2][:green] ? ',G:'+chunk[2][:green].to_s : ''} #{chunk[2][:blue] ? ',B:'+chunk[2][:blue].to_s : ''} #{chunk[2][:alpha] ? ',A:'+chunk[2][:alpha].to_s : ''} " when :pHYs contents = "pixel size = #{chunk[2][:x]}x#{chunk[2][:y]} pixels" when :cHRM contents = "chromaticities" when :gAMA contents = "file gamma = #{'%f' % chunk[2]}" when :IDAT contents = "PNG image data" when :tIME contents = "time of last modification = #{chunk[2].to_s}" when :tEXt contents = "standard text: [#{chunk[2][:keyword]}]=[#{chunk[2][:text]}]" when :zTXt contents = "standard compressed text: [#{chunk[2][:keyword]}]=[#{chunk[2][:text]}]" when :iTXt begin contents = Uconv.u8tosjis("international(#{chunk[2][:language_tag]}) text: [#{chunk[2][:keyword]}:#{chunk[2][:translated_keyword]}]=[#{chunk[2][:text]}]") rescue Uconv::Error messageBox("Uconv Error.", self.caption, 0x30) p contents end when :IEND contents = "end-of-image marker" end @lview.addItem([schunk, attributes, length, crc, contents]) end end def update_caption self.caption = "PNGText - #{File.basename(@file.gsub(/\\/, '/'))} #{@edit ? '*' : ''}" end def load_png(file) @edit = false @open = true @mnuFileSave.state = 0 @mnuFileSaveAs.state = 0 @mnuEditAddText.state = 0 @mnuPopupAddText.state = 0 @mnuEditConvert.state = 0 begin @png = File.open(file, "rb") {|f| PNG.new(f) } @file = file update_caption refresh rescue PNG::InvalidPNGError p $!, $@ messageBox("InvalidPNGError", self.caption, 0x30) end end def self_dropfiles(files) load_png(files[0]) end end frm = VRLocalScreen.newform nil, nil, MainForm frm.create.show frm.load_png ARGV[0] if ARGV[0] VRLocalScreen.messageloop #VRLocalScreen.start(MainForm)