#!/usr/bin/perl #***************************************************************************** # # ooffice - Wrapper script for OpenOffice.org # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # #***************************************************************************** use strict; use MDK::Common; # Define OpenOffice.org version my $RPMVersion = "<OOVERSION>"; # Transform "X.Y" versions to "X.Y.0" my $Version = $RPMVersion . ($RPMVersion !~ /\d+\.\d+\.\d+/ ? ".0" : ""); # Define OpenOffice.org tree version my $TreeVersion = "<OOTREEVERSION>"; # Define user OOo versions file my $VersionFile = "$ENV{HOME}/.sversionrc"; # Define system installation directory my $SystemInstallDir = "<LIBDIR>/openoffice"; # Define user installation directory my $UserInstallDir = "$ENV{HOME}/.openoffice"; # Define user work directory ($HOME directory) my $UserWorkDir = $ENV{HOME}; # Define chkfontpath program my $ChkfontpathProgram = "/usr/sbin/chkfontpath"; # Define configimport tool my $ConfigImportProgram = "/usr/bin/ooconfigimport"; # Define language mapper tool my $LanguageMapperProgram = "$SystemInstallDir/program/oo_xlate_lang"; # Define OOo setup program my $SetupProgram = "$SystemInstallDir/program/setup"; # Define setup autoresponse file for user installation my $SetupConfig = "/etc/openoffice/autoresponse.conf"; # Define user Setup.xcu file my $SetupXCU = "$UserInstallDir/user/registry/data/org/openoffice/Setup.xcu"; # Define user Common.xcu file my $CommonXCU = "$UserInstallDir/user/registry/data/org/openoffice/Office/Common.xcu"; # Define system ooffice script config my $SystemOOfficeRC = "/etc/openoffice/openoffice.conf"; # Define user ooffice script config my $OOfficeRC = "$ENV{HOME}/.oofficerc"; # Define system psprint.conf my $SystemPsprintConf = "$SystemInstallDir/share/psprint/psprint.conf"; # Define user psprint.conf my $PsprintConf = "$UserInstallDir/user/psprint/psprint.conf"; # Define user mime.types my $MimeTypes = "$ENV{HOME}/.mime.types"; # Define if debug mode my $Debug = 0; #============================================================================= # Print debug output #============================================================================= sub debug { print @_ if $Debug; } #============================================================================= # Read versions config file #============================================================================= sub ReadVersionFile($) { my ($file) = @_; my $e; my %entries; foreach (cat_($file)) { chomp; if (/^\[(\w+)\]/) { $entries{$1} = $e = { }; } elsif (/^([^=]+)=([^\r\n]+)/) { $e->{$1} = $2; } } %entries; } #============================================================================= # Write versions config file #============================================================================= sub DoWriteVersionFile(%$) { my ($config, $file) = @_; my $F; if ($file) { open $F, ">$file" or die "Cant write to $file\n"; select $F; } local $\ = "\n"; while (my ($secname, $secvars) = each %$config) { print "[$secname]"; while (my ($key, $value) = each %$secvars) { print "$key=$value"; } # BUG: $_ is undeclared here (do you means 'print ""' instead in order to print a newline despite $\ ?): print; } if ($file) { close $F; select STDOUT; } } sub DumpVersionFile(%) { my (%config) = @_; DoWriteVersionFile \%config; } sub WriteVersionFile(%) { my (%config) = @_; DoWriteVersionFile \%config, $VersionFile; } #============================================================================= # Define default language -> country mappings #============================================================================= sub CountryOfLanguage($) { my ($lang) = @_; # Note if language is not in the table of exceptions below, it is # assumed that country is uc $lang my %countryOf = ("ar" => "EG", "en" => "US", "cs" => "CZ", "ca" => "ES", "da" => "DK", "el" => "GR", "et" => "EE", "eu" => "ES", "ja" => "JP", "ko" => "KR", "sv" => "SE"); $countryOf{$lang} || uc $lang; } #============================================================================= # Main #============================================================================= # Get global and user configs (user config file has precedence over system config). my %OOfficeConfig = getVarsFromSh($OOfficeRC); add2hash(\%OOfficeConfig, { getVarsFromSh($SystemOOfficeRC) }); # Parse command line arguments my @ooo_argv; my $override_lang; while ($ARGV[0]) { local $_ = shift; if (m/^--lang(=(\S+))?/) { $override_lang = $2 || shift; } elsif (m/^--debug(ger)?(=(.+))?/) { $ENV{SAL_DEBUGGER} = $3 || shift; } elsif (m/^--help$/) { print STDERR "Usage: $0 [<wrapper options>] [<OpenOffice.org options>] --help Print help option --lang <lang>[-<country>] Set language for the user interface This overrides automatic language setting from the following variables: LC_ALL, LC_CTYPE, LANG. <lang> is an ISO language code, possibly suffixed by ISO country code if necessary. --debugger <debugger> Run OpenOffice.org through the <debugger> This overrides debugger set from SAL_DEBUGGER variable. <debugger> shall contain the possible options to pass to the debugger. "; exit 0; } else { push @ooo_argv, $_; } } push @ooo_argv, "private:factory/s$1" if !@ooo_argv && $0 =~ m!/oo(calc|draw|impress|math|writer)$!; sub normalize { my ($lang) = @_; my $tmplang=$lang; $tmplang =~ tr/-/_/; $tmplang .= "_" . CountryOfLanguage $tmplang if $tmplang !~ /_/; (my $isocode = $tmplang) =~ s/([a-z]+)_.*/\1/; (my $oolang = $tmplang) =~ tr/_/-/; $oolang =~ s/\.UTF.*//; return ($lang, $oolang, $isocode); } # Get current language code my $lang = $override_lang || $OOfficeConfig{UI_LANG}; $lang = $ENV{LC_ALL} || $ENV{LC_CTYPE} || $ENV{LANG} || "en_US" if !$lang || $lang eq "AUTO"; my ($oolang, $isocode); ($lang, $oolang, $isocode) = normalize($lang); # Determine OOo prefix code and check we have the requested language # pack installed, otherwise default to one another preferably not # English first sub lang2prefix { (split /\s/, `$LanguageMapperProgram -p $_[0] 2> /dev/null`)[0] } sub prefix2lang { (split /\s/, `$LanguageMapperProgram -i $_[0] 2> /dev/null`)[0] } my $ooprefix = lang2prefix($oolang) || lang2prefix($isocode); my $res_dir = "$SystemInstallDir/program/resource"; if (! -f "$res_dir/ooo$TreeVersion$ooprefix.res") { my @res_paths = glob_("$res_dir/ooo$TreeVersion*.res"); # English is always 01 so first pick up 2nd element, if any (my $ooprefix_avail = $res_paths[1] || $res_paths[0]) =~ s|$res_dir/ooo$TreeVersion(\d+).res|\1|; ($lang, $oolang, $isocode) = normalize prefix2lang($ooprefix_avail); } # Remove any stale entry from versions file, if OOo was not already # installed from an MDK package. my ($UpdateMode, $FirstTimeInstall) = (0, undef); if (-r $VersionFile) { my %config = ReadVersionFile($VersionFile); my $versions = $config{Versions}; my $home = $versions->{"OpenOffice.org $Version"}; if ($home && $home ne "file://$UserInstallDir") { foreach (keys %$versions) { delete $versions->{$_} if $versions->{$_} eq $home; } WriteVersionFile %config; } # Check if we need to make an upgrade sub str2dec_version { if_($_[0] =~ /(\d+)\.(\d+)\.?(\d+)?/, "$1$2" . ($3 || 0)) } my $this_version = str2dec_version $Version; foreach (keys %$versions) { my $installed_version = str2dec_version $_; $UpdateMode |= $versions->{$_} eq "file://$UserInstallDir" && $installed_version < $this_version; } } # Perform a user installation or upgrade, if necessary. if ($UpdateMode || ! -d $UserInstallDir || ! -e "$UserInstallDir/soffice" || ! -e "$UserInstallDir/spadmin") { # Make sure to remove any reference to $UserInstallDir, from older # (and broken) installations if (-f $VersionFile) { my %config = ReadVersionFile($VersionFile); my $versions = $config{Versions}; my $changed = 0; foreach (keys %$versions) { if ($versions->{$_} eq "file://$UserInstallDir") { delete $versions->{$_}; $changed = 1; } } WriteVersionFile %config if $changed; } my $install_isocode = -e "$SystemInstallDir/program/instdb.ins.$oolang" ? $oolang : $isocode; # We can safely do the installation now die "Installation of OpenOffice.org $Version failed\n" if (system("$SetupProgram -R:$SetupConfig -LANG:$install_isocode")); # We have just installed the package $FirstTimeInstall = !$UpdateMode; # FIXME: Clean user's .mime.types, OOo has a tendency to append # that at each installation. The fix should be in OOo code. output $MimeTypes, uniq cat_($MimeTypes); } # Clean the installation version tags. if (-f $VersionFile) { my %config = ReadVersionFile($VersionFile); my $versions = $config{Versions}; my $changed = 0; foreach (keys %$versions) { if (/^OpenOffice.org [.0-9]+/ && $_ ne "OpenOffice.org $Version" && $versions->{$_} eq "file://$UserInstallDir") { delete $versions->{$_}; $changed = 1; } } # Add current version tag if it does not exist already. This # occurs when you upgrade from 1.0 to 1.0.1 for example. # # FIXME: Besides, with some localizations, installer is generating # "OpenOffice.org 1.1" entries instead of "OpenOffice.org 1.1.0". if (!$versions->{"OpenOffice.org $Version"}) { $versions->{"OpenOffice.org $Version"} = "file://$UserInstallDir"; $changed = 1; } WriteVersionFile %config if $changed; } else { # The versions file is bound to exist unless we (or the user) # nuked it somehow. Regenerate it. We already have the # installation directory at this point. WriteVersionFile Versions => { "OpenOffice.org $Version" => "file://$UserInstallDir" }; } # Remove broken links and previous dictionaries symlinks. # As of 1.0.1-1mdk, dictionaries stuff is global in /usr/share/dict/ooo. my @stale_dictentries; foreach (glob_("$UserInstallDir/user/wordbook/*.aff")) { if (-l $_) { (my $entry = $_) =~ s|.*/([^/]+)\.aff|\1|; push @stale_dictentries, $entry; unlink $_, ((dirname $_) . "/$entry.dic"); } } my $dict_list = "$UserInstallDir/user/wordbook/dictionary.lst"; my $dict_pattern = join "|", @stale_dictentries; substInFile { s|^(\s*DICT.*\s($dict_pattern))$|# Removed: \1| } $dict_list if ($dict_pattern && -f $dict_list); # Remove dictionary.lst if it no longer contains any valid dictionary # (DICT) or hyphenation (HYPH) entries. unlink $dict_list if !any { /^\s*(DICT|HYPH)/ } cat_($dict_list); # Expand TrueType and Type1 font paths my @exclude_fontpaths = "/usr/share/fonts/ttf/japanese"; my @fontpath; if (! -x $ChkfontpathProgram) { # OOo now correctly grabs chkfontpath output, so only add # "well-known" TrueType and Type1 font paths if that program is # not available push @fontpath, grep { -d $_ && !member($_, @exclude_fontpaths) } (glob_("/usr/share/fonts/ttf/*"), "/usr/X11R6/lib/X11/fonts/TTF", "/usr/X11R6/lib/X11/fonts/drakfont/ttf", "/usr/X11R6/lib/X11/fonts/Type1", "/usr/share/fonts/default/Type1"); } $ENV{SAL_FONTPATH} = join(";", @fontpath) . ";$ENV{SAL_FONTPATH}" if @fontpath; # User work dir is correctly defined in *.xml files, so we no longer # need the symlink, which actually is recursive for other programs if (-l "$UserInstallDir/user/work") { # Remove garbage from older installations/versions. Assume the # user hasn't changed the symlink. i.e. we only clean up original # defaults. unlink "$UserInstallDir/user/work" if readlink("$UserInstallDir/user/work") eq $UserWorkDir; } #============================================================================= # Define language -> font mappings #============================================================================= sub FontOfLanguage($) { my ($lang) = @_; my %fontOf = ((map { ("zh-$_" => "AR PL New Sung" ) } ("CN", "SG")), (map { ("zh-$_" => "AR PL New Sung") } ("TW", "HK", "MO")), "en" => "Bitstream Vera Sans", "pt" => "Bitstream Vera Sans", "nl" => "Bitstream Vera Sans", "fr" => "Bitstream Vera Sans", "es" => "Bitstream Vera Sans", "fi" => "Bitstream Vera Sans", "it" => "Bitstream Vera Sans", "da" => "Bitstream Vera Sans", "sv" => "Bitstream Vera Sans", "de" => "Bitstream Vera Sans", "eu" => "Bitstream Vera Sans", "cy" => "Bitstream Vera Sans", "pt-BR" => "Bitstream Vera Sans", "et" => "Bitstream Vera Sans", "el" => "KerkisSans", "ja" => "Kochi Gothic", "ko" => "Baekmuk Gulim", "ru" => "Nimbus Sans L", "ar" => "KacstBook", "th" => "Norasi"); $fontOf{$lang} || $fontOf{(split /[-_]/, $lang)[0]} || "Luxi Sans"; } #============================================================================= # Return the MTIME of a file, -1 if it does not exist #============================================================================= sub TimeStamp($) { my ($file) = @_; -f $file ? (stat($file))[9] : -1; } #============================================================================= # Determine a proper font scaling from current screen resolution #============================================================================= sub FontScalingFromResolution() { # Get current resolution of screen #0 my $width; foreach (reverse cat_("LC_ALL=C xdpyinfo |")) { $width = $1 if m|^\s+dimensions:\s+([0-9]+)x[0-9]+ pixels|; } # Map to a scale value my %ScaleOfResolution = ("640" => "120", "800" => "120", "1024" => "110", "1152" => "110"); $ScaleOfResolution{$width} || "100"; } #============================================================================= # Classify language code into { LAT, CJK, CTL } #============================================================================= sub ClassifyLanguage($) { my $lang = (split /[-_]/, $_[0])[0]; my %langClass = ((map { ($_ => "CJK") } ("zh", "ja", "ko")), (map { ($_ => "CTL") } ("ar", "th"))); $langClass{$lang} || "LAT"; } #============================================================================= # Merge configurations #============================================================================= sub ConfigImport($) { my ($node) = @_; my $temp_file = "/tmp/oooconf-$$.xcu"; output $temp_file, $node; my $rc = `$ConfigImportProgram $temp_file 2> /dev/null`; unlink $temp_file; return $rc; } #============================================================================= # Misc configuration setup in user *.xml files #============================================================================= # Get old OOo locale my $old_oolang = $oolang; foreach (cat_ $SetupXCU) { if (/<prop oor:name="ooLocale"/ .. m!</prop>!) { m!<value>([\w-]+)</value>! and $old_oolang = $1; } } $old_oolang .= "-" . CountryOfLanguage $old_oolang if $old_oolang !~ /-/; # Update user configuration if locale changed if ($FirstTimeInstall || $lang && $oolang ne $old_oolang) { # Import new locale configuration my $locale_node = <<EOF; <?xml version="1.0" encoding="UTF-8"?> <oor:node xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="Setup" oor:package="org.openoffice"> <node oor:name="L10N"> <prop oor:name="ooLocale" oor:type="xs:string"> <value>$oolang</value> </prop> </node> </oor:node> EOF ConfigImport $locale_node; # Import new default language for documents my $lat_oolang = ClassifyLanguage $oolang eq "LAT" ? $oolang : ""; my $cjk_oolang = ClassifyLanguage $oolang eq "CJK" ? $oolang : ""; my $ctl_oolang = ClassifyLanguage $oolang eq "CTL" ? $oolang : ""; my $default_doc_lang_node = <<EOF; <?xml version="1.0" encoding="UTF-8"?> <oor:node xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="Linguistic" oor:package="org.openoffice.Office"> <node oor:name="General"> <prop oor:name="DefaultLocale" oor:type="xs:string"> <value>$lat_oolang</value> </prop> <prop oor:name="DefaultLocale_CJK" oor:type="xs:string"> <value>$cjk_oolang</value> </prop> <prop oor:name="DefaultLocale_CTL" oor:type="xs:string"> <value>$ctl_oolang</value> </prop> </node> </oor:node> EOF ConfigImport $default_doc_lang_node; # Prepare CJK configuration my $is_cjk = ClassifyLanguage($oolang) eq "CJK" ? "true" : "false"; my $cjk_node = <<EOF; <node oor:name="CJK"> <prop oor:name="CJKFont" oor:type="xs:boolean"> <value>$is_cjk</value> </prop> <prop oor:name="VerticalText" oor:type="xs:boolean"> <value>$is_cjk</value> </prop> <prop oor:name="AsianTypography" oor:type="xs:boolean"> <value>$is_cjk</value> </prop> <prop oor:name="JapaneseFind" oor:type="xs:boolean"> <value>$is_cjk</value> </prop> <prop oor:name="Ruby" oor:type="xs:boolean"> <value>$is_cjk</value> </prop> <prop oor:name="ChangeCaseMap" oor:type="xs:boolean"> <value>$is_cjk</value> </prop> <prop oor:name="DoubleLines" oor:type="xs:boolean"> <value>$is_cjk</value> </prop> <prop oor:name="EmphasisMarks" oor:type="xs:boolean"> <value>$is_cjk</value> </prop> <prop oor:name="VerticalCallOut" oor:type="xs:boolean"> <value>$is_cjk</value> </prop> </node> EOF # Prepare CTL configuration my $is_ctl = ClassifyLanguage($oolang) eq "CTL" ? "true" : "false"; my $ctl_node = <<EOF; <node oor:name="CTL"> <prop oor:name="CTLFont" oor:type="xs:boolean"> <value>$is_ctl</value> </prop> <prop oor:name="CTLSequenceChecking" oor:type="xs:boolean"> <value>$is_ctl</value> </prop> <prop oor:name="CTLCursorMovement" oor:type="xs:int"> <value>0</value> </prop> <prop oor:name="CTLTextNumerals" oor:type="xs:int"> <value>0</value> </prop> </node> EOF # We changed locale, force font substitutions my $font_subst_node = <<EOF; <node oor:name="Font"> <node oor:name="Substitution"> <prop oor:name="Replacement" oor:type="xs:boolean"> <value>true</value> </prop> <node oor:name="FontPairs"> EOF my %font_substs = ("Andale Sans UI" => { id => "_0", always => "true" }, "Albany" => { id => "_1", always => "false" }, "Arial" => { id => "_2", always => "false" }); { # HACK: get correct IDs from current Common.xcu file my $id; foreach (cat_($CommonXCU)) { if (/^\s+<node oor:name="(_\d+)" oor:op="replace">/) { $id = $1; } elsif (/^\s+<prop oor:name="ReplaceFont" oor:type="xs:string">/ ... m!^\s+</prop>!) { if (m!<value>([\w\s]+)</value>!) { my $replace = $1; $font_substs{$replace}{id} = $id if exists $font_substs{$replace}; } } } } # Apply font substitutions foreach my $replace (keys %font_substs) { my ($always, $substitute) = ($font_substs{$replace}{always}, FontOfLanguage $oolang); my $font_id = $font_substs{$replace}{id}; $font_subst_node .= <<EOF; <node oor:name="$font_id" oor:op="replace"> <prop oor:name="Always" oor:type="xs:boolean"> <value>$always</value> </prop> <prop oor:name="OnScreenOnly" oor:type="xs:boolean"> <value>true</value> </prop> <prop oor:name="ReplaceFont" oor:type="xs:string"> <value>$replace</value> </prop> <prop oor:name="SubstituteFont" oor:type="xs:string"> <value>$substitute</value> </prop> </node> EOF } $font_subst_node .= <<EOF; </node> </node> </node> EOF # Disable font substitution table for now as it should be possible # to tune VCL.xcu defaults. # $font_subst_node = ""; # Import new configuration my $common_nodes = <<EOF; <?xml version="1.0" encoding="UTF-8"?> <oor:node xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="Common" oor:package="org.openoffice.Office"> $font_subst_node <node oor:name="I18N"> $cjk_node $ctl_node </node> </oor:node> EOF ConfigImport $common_nodes; # Default to paper size from locale setting my $paper_size = $OOfficeConfig{PAPER_SIZE}; # Get paper size dimension according to language set if ($paper_size eq "AUTO") { my ($width, $height); foreach (cat_("LC_ALL=$lang /usr/bin/locale -k LC_PAPER |")) { if (/^width=([0-9]+)/) { $width = $1; } elsif (/^height=([0-9]+)/) { $height = $1; } } my %PaperSizeOfDimensions = ("210x297" => "A4", "216x279" => "Letter"); $paper_size = $PaperSizeOfDimensions{"${width}x${height}"} || "A4"; } if ($paper_size) { # Always grab the newer system configuration as new printers # may have been configured in the meantime. output($PsprintConf, cat_($SystemPsprintConf), "\n") if TimeStamp($SystemPsprintConf) > TimeStamp($PsprintConf) || !any { /PDF.+Converter (Screen|Press)/ } cat_($PsprintConf); # Change "Generic Printer" section as this is the only valid # one provided that no other printer is configured and setup # as default, if any. Otherwise, printerdrake already # generates printer configuration with the right paper size. # NOTE: also change for PDF converters foreach my $category ("Generic Printer", "PDF 1.4 Converter Screen", "PDF 1.4 Converter Press", "Generic PostScript output (distillable)") { # psprint.conf happens to be a "Windows"-style file update_gnomekderc $PsprintConf, $category, PPD_PageSize => $paper_size; } } } my %file_formats = ( calc => 'DOC_DEFAULTS_CALC', impress => 'DOC_DEFAULTS_IMPRESS', writer => 'DOC_DEFAULTS_WRITER', ); my $prog = $1 if $0 =~ /oo(calc|writer|impress)$/; # only check for user config when looking at file format settings: my %user_config = getVarsFromSh($OOfficeRC); if ($prog && !$user_config{$file_formats{$prog}}) { # run wizard: system("drakoo $prog"); # reread just updated config: %user_config = getVarsFromSh($OOfficeRC); add2hash(\%user_config, \%OOfficeConfig); %OOfficeConfig = %user_config; } # Handle doc defaults $ENV{OOO_MS_DEFAULTS} = 1 if $OOfficeConfig{DOC_DEFAULTS} eq "MS"; $ENV{OOO_MS_DEFAULTS_CALC} = 1 if $OOfficeConfig{DOC_DEFAULTS_CALC} eq "MS"; $ENV{OOO_MS_DEFAULTS_WRITER} = 1 if $OOfficeConfig{DOC_DEFAULTS_WRITER} eq "MS"; $ENV{OOO_MS_DEFAULTS_IMPRESS} = 1 if $OOfficeConfig{DOC_DEFAULTS_IMPRESS} eq "MS"; # In some cases, OOo requests language from environment variables and # first checks for LANG. Override it thusly. # $ENV{LANG} = "$lang"; # Execute custom config script (macros, paper format, etc), in a way that # the script can be included in an addon package for mass deployment. system "/etc/openoffice/customconfig" if -e "/etc/openoffice/customconfig"; # And here we go. ;-) exec "$UserInstallDir/soffice", @ooo_argv if !$Debug; # Local variables: # tab-width: 4 # indent-tabs-mode: nil # End: