#!/usr/bin/env ruby # -*- ruby -*- GC.disable require "rbconfig" require "rabbit/console" require "rabbit/source" require "rabbit/renderer" require "rabbit/front" def parse_args(args=ARGV, logger=nil) Rabbit::Console.parse!(args, logger) do |opts, options| options.theme = "default" options.theme_specified = false options.base = nil options.source_type = Rabbit::Source::File options.full_screen = false options.index_mode = false options.geometry = nil options.width = 800 options.height = 600 options.paper_width = nil options.paper_height = nil options.save_as_image = false options.saved_image_base_name = nil options.saved_image_type = "png" options.output_html = false options.output_index_html = false options.rss_base_uri = nil options.encoding = nil options.print = false options.print_out_filename = nil options.slides_per_page = 1 options.draw_scaled_image = true options.margin_left = nil options.margin_right = nil options.margin_top = nil options.margin_bottom = nil options.page_margin_left = nil options.page_margin_right = nil options.page_margin_top = nil options.page_margin_bottom = nil options.use_druby = true options.druby_uri = "druby://:10101" options.output_druby_uri = false options.use_soap = false options.soap_host = "0.0.0.0" options.soap_port = 10103 options.use_xmlrpc = false options.xmlrpc_host = "0.0.0.0" options.xmlrpc_port = 10104 options.server = false options.default_public_level = "strict" options.public_level = nil options.comment_source = nil options.comment_encoding = nil options.migemo_dictionary_search_path = [ File.join(Config::CONFIG["prefix"], "share"), File.join("", "usr", "local", "share"), File.join("", "usr", "share"), ].uniq options.migemo_dictionary_name = "migemo-dict" options.use_gl = false options.show_native_window_id = false options.keep_above = false options.source_filename = nil opts.banner = "#{opts.banner} [SOURCE_INFOS]" opts.category _("Theme") opts.on("-I", "--include=PATH", _("Add [PATH] to load path.")) do |path| $LOAD_PATH.unshift(path) end opts.on("-t", "--theme=THEME", _("Use [THEME] as theme."), "(#{options.theme})") do |theme| options.theme = theme options.theme_specified = true end opts.category _("Source") source_type_names = Rabbit::Source.types.collect do |x| Rabbit::Console.get_last_name(x).downcase end source_type_descs = Rabbit::Source.types.collect do |x| message = _("When select %s\nspecify %s\nas [SOURCE_INFOS].") type = Rabbit::Console.get_last_name(x) message = message % [type, _(x.initial_args_description)] message.split(/\n/) + [" "] end.flatten opts.on("-T", "--type=TYPE", source_type_names, _("Specify source type as [TYPE]."), _("Select from [%s].") % source_type_names.join(', '), _("Note: case insensitive."), "(#{Rabbit::Console.get_last_name(options.source_type)})", _("(ARGV: if no filename is given)"), " ", *source_type_descs) do |source_type| options.source_type = Rabbit::Source.types.find do |t| Rabbit::Console.get_last_name(t).downcase == source_type.downcase end end opts.on("-e", "--encoding=ENCODING", _("Specify source encoding as [ENCODING]."), _("(auto)")) do |encoding| options.encoding = encoding end opts.on("-B", "--base=BASE", _("Specify base URI or path of source as [BASE]."), _("(auto)")) do |base| options.base = base end opts.category _("Initial state") opts.on("-f", "--[no-]full-screen", _("Toggle full screen mode."), "(#{options.full_screen ? 'on' : 'off'})") do |bool| options.full_screen = bool end opts.on("--[no-]index-mode", _("Toggle index mode."), "(#{options.index_mode ? 'on' : 'off'})") do |bool| options.index_mode = bool end opts.category _("Size") opts.on("-g", "--geometry=GEOMETRY", _("Set window geometry [GEOMETRY]."), _("Format: WIDTHxHEIGHT+X+Y"), "(#{options.geometry.inspect})") do |geometry| options.geometry = geometry end opts.on("-w", "--width=WIDTH", Integer, _("Set window width to [WIDTH]."), "(#{options.width})") do |width| options.width = width end opts.on("-h", "--height=HEIGHT", Integer, _("Set window height to [HEIGHT]."), "(#{options.height})") do |height| options.height = height end message = _("Set window width and height to\n" \ "[WIDTH] and [HEIGHT].") message = message.split(/\n/) + ["(#{options.width},#{options.height})"] opts.on("-S", "--size=WIDTH,HEIGHT", Array, *message) do |size| width, height = size.collect{|x| Integer(x)} options.width = width options.height = height end opts.category _("Save") opts.on("-s", "--save-as-image", _("Save as image and exit.")) do options.save_as_image = true end opts.on("-i", "--saved-image-type=TYPE", _("Specify saved image type as [TYPE]."), "(#{options.saved_image_type})") do |t| options.saved_image_type = t end opts.on("-b", "--saved-image-base-name=BASE_NAME", "--saved-image-basename=BASE_NAME", _("Specify saved image base name as [BASE_NAME]."), "(" + _("Title of slide") + ")") do |b| options.saved_image_base_name = b end opts.on("--[no-]output-html", _("Output HTML for viewing saved images."), "(#{options.output_html})") do |bool| options.output_html = bool end opts.on("--[no-]output-index-html", _("Output index HTML for navigating slides."), "(#{options.output_index_html})") do |bool| options.output_index_html = bool end opts.on("--rss-base-uri=URI", _("Specify base URI of RSS as [URI]."), _("RSS is generated only when HTML is output."), "(#{options.rss_base_uri})") do |uri| options.rss_base_uri = uri end opts.on("--source-filename=FILENAME", _("Specify source filenam as [FILENAME]."), "(#{options.source_filename})") do |filename| options.source_filename = filename end opts.category _("Print") opts.on("-p", "--print", _("Print and exit.")) do options.print = true end opts.on("-o", "--output-filename=FILENAME", _("Specify printed out filename as [FILENAME]."), "(\#{%s}.pdf)" % _("Title of slide")) do |f| options.print_out_filename = f end opts.on("--slides-per-page=SLIDES", Integer, _("Set slides per page."), "(1)") do |slides| options.slides_per_page = slides end opts.on("--[no-]draw-scaled-image", _("Draw scaled image."), _("Better look for displaying but lesser look for printing."), "(#{options.draw_scaled_image})") do |boolean| options.draw_scaled_image = boolean end opts.category _("Paper") opts.on("--paper-width=WIDTH", Integer, _("Set paper width to [WIDTH] Pt."), _("(landscape A4 width)")) do |width| options.paper_width = width end opts.on("--paper-height=HEIGHT", Integer, _("Set paper height to [HEIGHT] Pt."), _("(landscape A4 height)")) do |height| options.paper_height = height end message = _("Set paper width and height to\n" \ "[WIDTH] Pt and [HEIGHT] Pt.") message = message.split(/\n/) + [_("(landscape A4 size)")] opts.on("--paper-size=WIDTH,HEIGHT", Array, *message) do |size| width, height = size.collect{|x| Integer(x)} options.paper_width = width options.paper_height = height end opts.category _("Margin") opts.on("--margin-left=MARGIN", Integer, _("Set left margin for slides per page mode print."), _("(auto)")) do |margin| options.margin_left = margin end opts.on("--margin-right=MARGIN", Integer, _("Set right margin for slides per page mode print."), _("(auto)")) do |margin| options.margin_right = margin end opts.on("--margin-top=MARGIN", Integer, _("Set top margin for slides per page mode print."), _("(auto)")) do |margin| options.margin_top = margin end opts.on("--margin-bottom=MARGIN", Integer, _("Set bottom margin for slides per page mode print."), _("(auto)")) do |margin| options.margin_bottom = margin end margin1 = _("[ALL]") margin2 = _("[TOP_BOTTOM],[LEFT_RIGHT]") margin3 = _("[TOP],[LEFT_RIGHT],[BOTTOM]") margin4 = _("[TOP],[RIGHT],[BOTTOM],[LEFT]") opts.on("--margin={#{margin1}|#{margin2}|#{margin3}|#{margin4}}", Array, _("Set margin for slides per page mode print.")) do |margins| begin top, right, bottom, left = Rabbit::Utils.parse_four_way(margins) options.margin_top = top options.margin_right = right options.margin_bottom = bottom options.margin_left = left rescue ArgumentError raise OptionParser::InvalidArgument.new(margins) end end opts.on("--page-margin-left=MARGIN", Integer, _("Set left page margin."), _("(auto)")) do |margin| options.page_margin_left = margin end opts.on("--page-margin-right=MARGIN", Integer, _("Set right page margin."), _("(auto)")) do |margin| options.page_margin_right = margin end opts.on("--page-margin-top=MARGIN", Integer, _("Set top page margin."), _("(auto)")) do |margin| options.page_margin_top = margin end opts.on("--page-margin-bottom=MARGIN", Integer, _("Set bottom page margin."), _("(auto)")) do |margin| options.page_margin_bottom = margin end opts.on("--page-margin={#{margin1}|#{margin2}|#{margin3}|#{margin4}}", Array, _("Set page margin.")) do |margins| begin top, right, bottom, left = Utils.parse_four_dimensions(margins) options.page_margin_top = top options.page_margin_right = right options.page_margin_bottom = bottom options.page_margin_left = left rescue ArgumentError raise OptionParser::InvalidArgument.new(margins) end end opts.category _("dRuby") opts.on("--[no-]use-druby", _("Specify whether to use dRuby."), "(#{options.use_druby})") do |bool| options.use_druby = bool end opts.on("--druby-uri=URI", _("Specify dRuby URI."), "(#{options.druby_uri})") do |uri| options.druby_uri = uri if uri end opts.on("--[no-]output-druby-uri", _("Specify whether to output dRuby URI."), "(#{options.output_druby_uri})") do |bool| options.output_druby_uri = bool end opts.category _("SOAP") opts.on("--[no-]use-soap", _("Specify whether to use SOAP."), "(#{options.use_soap})") do |bool| options.use_soap = bool end opts.on("--soap-host=HOST", _("Specify SOAP host as [HOST]."), "(#{options.soap_host})") do |port| begin options.soap_host = host end end opts.on("--soap-port=PORT", _("Specify SOAP port as [PORT]."), "(#{options.soap_port})") do |port| begin options.soap_port = Integer(port) if port rescue ArgumentError raise OptionParser::InvalidArgument.new(port) end end opts.category _("XML-RPC") opts.on("--[no-]use-xmlrpc", _("Specify whether to use XML-RPC."), "(#{options.use_xmlrpc})") do |bool| options.use_xmlrpc = bool end opts.on("--xmlrpc-host=HOST", _("Specify XML-RPC host as [HOST]."), "(#{options.xmlrpc_host})") do |port| begin options.xmlrpc_host = host end end opts.on("--xmlrpc-port=PORT", _("Specify XML-RPC port as [PORT]."), "(#{options.xmlrpc_port})") do |port| begin options.xmlrpc_port = Integer(port) if port rescue ArgumentError raise OptionParser::InvalidArgument.new(port) end end opts.category _("Server") opts.on("--[no-]server", _("Specify whether to run as server."), "(#{options.server})") do |bool| options.server = bool end opts.category _("Public level") levels = Rabbit::Front::PublicLevel.constants.sort_by do |const| Rabbit::Front::PublicLevel.const_get(const) end.collect do |const| const.to_s.downcase.gsub(/_/, "-") end messages = [_("Specify public level.")] messages << _("Select from the following:") messages << "[" messages << " " levels[0..-2].each do |level| messages.last << "#{level}, " messages << " " if messages.last.size > 30 end messages.last << levels.last messages << "]" messages << _("(#{options.default_public_level})") opts.on("--public-level=LEVEL", levels, *messages) do |level| options.public_level = level end opts.category _("Comment") opts.on("--comment-source=FILE", _("Specify initial comment source."), _("(default source)")) do |name| options.comment_source = name end opts.on("--comment-encoding=ENCODING", _("Specify comment source encoding."), _("(auto)")) do |encoding| options.comment_encoding = encoding end opts.category _("Migemo") search_path = options.migemo_dictionary_search_path.join(', ') opts.on("--migemo-dictionary-search-path=PATH1,PATH2,...", Array, _("Specify search paths for Migemo static dictionary."), _("(#{search_path})")) do |path| options.migemo_dictionary_search_path = path end opts.on("--migemo-dictionary-name=NAME", Array, _("Specify static dictionary name for Migemo."), _("(#{options.migemo_dictionary_name})")) do |name| options.migemo_dictionary_name = name end opts.category _("3D") opts.on("--[no-]use-gl", _("Specify whether to use OpenGL if available."), "(#{options.use_gl})") do |bool| options.use_gl = bool end opts.category _("Display") opts.on("--[no-]keep-above", _("Specify whether to keep above window."), "(#{options.keep_above})") do |bool| options.keep_above = bool end opts.category _("Others") opts.on("--[no-]show-native-window-id", _("Show a native window ID of the Rabbit window if available."), _("e.g. The ID is the ID of X resource on X window system."), "(#{options.show_native_window_id})") do |bool| options.show_native_window_id = bool end end end def make_canvas(options, logger, renderer) args = [ logger, renderer, options.comment_source, options.comment_encoding, ] Rabbit::Canvas.new(*args) end def make_source(options, argv, logger) if options.source_type == Rabbit::Source::File and argv.empty? options.source_type = Rabbit::Source::ARGF end if options.source_type == Rabbit::Source::ARGF infos = [ARGF] else infos = argv end options.source_type.new(options.encoding, logger, *infos) end def make_front(canvas, options) level = options.public_level if options.source_type == Rabbit::Source::Memory or options.server level ||= "all" end level ||= options.default_public_level level = level.gsub(/-/, "_").upcase canvas.front(Rabbit::Front::PublicLevel.const_get(level)) end def apply_theme_if_need(target, options) target.apply_theme(options.theme) if options.theme_specified end def parse(target, options, logger, background=false) source = make_source(options, ARGV, logger) source.base = options.base if options.base if background callback = Rabbit::Utils.process_pending_events_proc else callback = nil end target.parse(source, callback) end def setup_image_info(target, options) target.saved_image_type = options.saved_image_type target.saved_image_base_name = options.saved_image_base_name target.output_html = options.output_html target.output_index_html = options.output_index_html target.rss_base_uri = options.rss_base_uri target.source_filename = options.source_filename end def setup_print_info(target, options) target.filename = options.print_out_filename target.slides_per_page = options.slides_per_page target.draw_scaled_image = options.draw_scaled_image end def setup_size(target, options) target.width = options.width target.height = options.height end def setup_paper_size(target, options) target.paper_width = options.paper_width target.paper_height = options.paper_height target.page_margin_left = options.page_margin_left target.page_margin_right = options.page_margin_right target.page_margin_top = options.page_margin_top target.page_margin_bottom = options.page_margin_bottom target.margin_left = options.margin_left target.margin_right = options.margin_right target.margin_top = options.margin_top target.margin_bottom = options.margin_bottom end def setup_migemo_info(target, options) target.migemo_dictionary_search_path = options.migemo_dictionary_search_path target.migemo_dictionary_name = options.migemo_dictionary_name end def setup_3d_info(target, options) target.use_gl = options.use_gl end def setup_druby(front, options, logger) require "drb/drb" begin DRb.start_service(options.druby_uri, front) logger.info(DRb.uri) if options.output_druby_uri rescue SocketError logger.error($!) rescue Errno::EADDRINUSE logger.error(_("dRuby URI <%s> is in use.") % options.druby_uri) end end def setup_soap(front, options, logger) require "rabbit/soap/server" thread = nil begin config = { :BindAddress => options.soap_host, :Port => options.soap_port, :AddressFamily => Socket::AF_INET, :Logger => logger, } server = Rabbit::SOAP::Server.new(front, config) prev = trap(:INT) {server.shutdown; trap(:INT, prev)} thread = Thread.new {server.start} rescue Errno::EADDRINUSE logger.error(_("port <%s> for SOAP is in use.") % options.soap_port) end thread end def setup_xmlrpc(front, options, logger) require "rabbit/xmlrpc/server" thread = nil begin config = { :BindAddress => options.xmlrpc_host, :Port => options.xmlrpc_port, :AddressFamily => Socket::AF_INET, :Logger => logger, } server = Rabbit::XMLRPC::Server.new(front, config) prev = trap(:INT) {server.shutdown; trap(:INT, prev)} thread = Thread.new {server.start} rescue Errno::EADDRINUSE logger.error(_("port <%s> for XML-RPC is in use.") % options.xmlrpc_port) end thread end def do_print(options, logger) renderer = Rabbit::Renderer.printable_renderer(options.slides_per_page) canvas = make_canvas(options, logger, renderer) setup_paper_size(canvas, options) setup_print_info(canvas, options) setup_3d_info(canvas, options) apply_theme_if_need(canvas, options) parse(canvas, options, logger) canvas.print canvas.quit rescue Rabbit::NoPrintSupportError logger.error($!.message) end def do_save_as_image(options, logger) Rabbit::Renderer::Pixmap.init Rabbit.gui_init canvas = make_canvas(options, logger, Rabbit::Renderer::Pixmap) setup_size(canvas, options) setup_image_info(canvas, options) setup_print_info(canvas, options) setup_paper_size(canvas, options) setup_3d_info(canvas, options) apply_theme_if_need(canvas, options) parse(canvas, options, logger) canvas.activate("ToggleIndexMode") if options.index_mode canvas.save_as_image canvas.quit end def do_display(options, logger) Rabbit::Renderer::Display.init Rabbit.gui_init canvas = make_canvas(options, logger, Rabbit::Renderer::Display) frame = Rabbit::Frame.new(logger, canvas) frame.geometry = options.geometry frame.force_keep_above = options.keep_above setup_paper_size(canvas, options) setup_image_info(canvas, options) setup_print_info(canvas, options) setup_migemo_info(canvas, options) setup_3d_info(canvas, options) frame.init_gui(options.width, options.height, true) frame.fullscreen if options.full_screen native_window = frame.window.window if options.show_native_window_id and native_window.respond_to?(:xid) logger.info(_("Window ID: %d") % native_window.xid) end apply_theme_if_need(frame, options) parse(frame, options, logger, !Rabbit::Utils.windows?) canvas.activate("ToggleIndexMode") if options.index_mode front = make_front(canvas, options) setup_druby(front, options, logger) if options.use_druby setup_soap(front, options, logger) if options.use_soap setup_xmlrpc(front, options, logger) if options.use_xmlrpc Gtk.main end def do_server(options, logger) Rabbit.gui_init # GLib::Log.cancel_handler # GLib::Log.set_handler(nil, GLib::Log::LEVEL_ERROR) canvas = make_canvas(options, logger, Rabbit::Renderer::Pixmap) setup_size(canvas, options) setup_paper_size(canvas, options) setup_image_info(canvas, options) setup_print_info(canvas, options) setup_3d_info(canvas, options) apply_theme_if_need(canvas, options) parse(canvas, options, logger) soap_server_thread = nil xmlrpc_server_thread = nil front = make_front(canvas, options) setup_druby(front, options, logger) if options.use_druby if options.use_soap soap_server_thread = setup_soap(front, options, logger) end if options.use_xmlrpc xmlrpc_server_thread = setup_xmlrpc(front, options, logger) end soap_server_thread.join if soap_server_thread xmlrpc_server_thread.join if xmlrpc_server_thread if options.use_druby prev = trap(:INT) do logger.info(_("going to shutdown...")) DRb.thread.exit logger.info(_("DRb.thread done.")) trap(:INT, prev) end DRb.thread.join end end def main options, logger = parse_args require "rabbit/canvas" GC.enable if options.save_as_image do_save_as_image(options, logger) elsif options.print do_print(options, logger) elsif options.server do_server(options, logger) else do_display(options, logger) end end main