#!/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 XML::Twig; use MDK::Common; # Define OpenOffice.org version my $Version = "<OOVERSION>"; # Define user OOo versions file my $VersionFile = "$ENV{HOME}/.sversionrc"; # Define system installation directory my $SystemInstallDir = "/usr/lib/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 OOo setup program my $SetupProgram = "$SystemInstallDir/program/setup"; # Define setup autoresponse file for user installation my $SetupConfig = "/etc/openoffice/autoresponse.conf"; # Define user Setup.xml file my $SetupXML = "$UserInstallDir/user/config/registry/instance/org/openoffice/Setup.xml"; # Define user Common.xml file my $CommonXML = "$UserInstallDir/user/config/registry/instance/org/openoffice/Office/Common.xml"; # Define user Linguistic.xml file my $LinguisticXML = "$UserInstallDir/user/config/registry/instance/org/openoffice/Office/Linguistic.xml"; # Define global Linguistic.xml file my $SystemLinguisticXML = "$SystemInstallDir/share/config/registry/instance/org/openoffice/Office/Linguistic.xml"; # 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) = @_; local *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"; } 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", "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 = (if_(-f "$SystemOOfficeRC", getVarsFromSh("$SystemOOfficeRC")), if_(-f "$OOfficeRC", getVarsFromSh("$OOfficeRC"))); # Parse command line arguments my @ooo_argv; my $override_lang; while ($ARGV[0]) { $_ = 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)$/); # Get current language code my $lang = $override_lang || $ENV{LC_ALL} || $ENV{LC_CTYPE} || $ENV{LANG} || "en_US"; $lang =~ tr/-/_/; $lang .= "_" . CountryOfLanguage $lang if ($lang !~ /_/); (my $isocode = $lang) =~ s/([a-z]+)_.*/\1/; (my $oolang = $lang) =~ tr/_/-/; # Remove any stale entry from versions file, if OOo was not already # installed from an MDK package. 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; } } # Perform a user installation, if necessary. if ( ! -d $UserInstallDir || ! -e "$UserInstallDir/soffice" || ! -e "$UserInstallDir/spadmin" ) { # First, make sure to remove any reference to $UserInstallDir, from older # (and broken) installation 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); } # We can safely do the installation now die "Installation of OpenOffice.org $Version failed\n" if (system("$SetupProgram -R:$SetupConfig -LANG:$isocode")); # 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. $versions->{"OpenOffice.org $Version"} = "file://$UserInstallDir" if (!$versions->{"OpenOffice.org $Version"}); 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 ( ! grep /^\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_USER} = join(";", @fontpath) . ";$ENV{SAL_FONTPATH_USER}" 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" ); } #============================================================================= # Dump XML tree into file specified by name #============================================================================= sub OutputXML($$) { my ($file, $tree) = @_; local *F; open F, ">$file" or die "Cannot write to file $file\n"; $tree->print(\*F); close F; } #============================================================================= # Define language -> font mappings #============================================================================= sub FontOfLanguage($) { my ($lang) = @_; my %fontOf = ((map { ("zh-$_" => "AR PL KaitiM GB" ) } ("CN", "SG")), (map { ("zh-$_" => "AR PL KaitiM Big5") } ("TW", "HK", "MO")), "el" => "KerkisSans", "ja" => "Kochi Gothic", "ko" => "Baekmuk Gulim", "ru" => "Nimbus Sans L", "ar" => "KacstBook"); $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, $height); foreach ( reverse cat_("LC_ALL=C xdpyinfo |") ) { ($width, $height) = ($1, $2) 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"; } #============================================================================= # Determine if language to use is CJK #============================================================================= sub IsCJK($) { my $lang = (split /[-_]/, $_[0])[0]; my @cjk_languages = ("zh", "ja", "ko"); member $lang, @cjk_languages; } #============================================================================= # Misc configuration setup in user *.xml files #============================================================================= # FIXME: handle Chinese for Singapore, Macau, Hong Kong if ( $lang && ( -e "$SystemInstallDir/help/$lang" || -e "$SystemInstallDir/help/$isocode" || -e "$SystemInstallDir/help/$oolang" ) ) { # Initialize Twig XML parser my $p = XML::Twig->new(pretty_print => "indented", keep_encoding => 1); # Adjust user interface language, in Setup.xml my $setup_xml = $p->parsefile("$SetupXML"); my $oolocale_node; if (! ($oolocale_node = ($setup_xml->get_xpath("/Setup/L10N/ooLocale"))[0]) ) { # Glue in new <L10N> section, "en-US" is the default locale my $new_l10n_node = << "EOF;"; <L10N> <ooLocale cfg:type="string">en-US</ooLocale> </L10N> EOF; $oolocale_node = XML::Twig->new()->parsestring($new_l10n_node)->root; $oolocale_node->paste($setup_xml->root); $oolocale_node = ($oolocale_node->descendants("ooLocale"))[0]; } my $old_oolang = $oolocale_node->text(); $oolocale_node->set_text("$oolang"); OutputXML "$SetupXML", $setup_xml; # Update default language for documents, in Linguistic.xml # Create file if it does not exist yet. my $linguistic_file; if ( -f ( $linguistic_file = "$LinguisticXML" ) || -f ( $linguistic_file = "$SystemLinguisticXML" ) ) { my $linguistic_xml = $p->parsefile("$linguistic_file"); my $lingu_node = ($linguistic_xml->get_xpath("/Linguistic/General"))[0]; my $dft_node = $lingu_node->first_child("DefaultLocale"); my $cjk_node = $lingu_node->first_child("DefaultLocale_CJK"); if (!$cjk_node) { $cjk_node = $dft_node->insert_new_elt("after", "DefaultLocale_CJK", { "cfg:type" => "string" } ); } $dft_node->set_text("$oolang"); $cjk_node->set_text(IsCJK "$oolang" ? "$oolang" : ""); $cjk_node->set_empty() if (! IsCJK $oolang); OutputXML "$LinguisticXML", $linguistic_xml; } my $common_xml_changed = 0; my $common_xml = $p->parsefile("$CommonXML"); # Add I18N/CJK settings my $cjk_node_value = IsCJK "$oolang" ? "true" : "false"; my $cjk_node; if (! ($cjk_node = ($common_xml->get_xpath("/Common/I18N/CJK"))[0]) ) { # Glue in new <I18N> section my $new_cjk_node = << "EOF;"; <I18N> <CJK> <AsianTypography cfg:type="boolean">$cjk_node_value</AsianTypography> <CJKFont cfg:type="boolean">$cjk_node_value</CJKFont> <ChangeCaseMap cfg:type="boolean">$cjk_node_value</ChangeCaseMap> <DoubleLines cfg:type="boolean">$cjk_node_value</DoubleLines> <EmphasisMarks cfg:type="boolean">$cjk_node_value</EmphasisMarks> <JapaneseFind cfg:type="boolean">$cjk_node_value</JapaneseFind> <Ruby cfg:type="boolean">$cjk_node_value</Ruby> <VerticalCallOut cfg:type="boolean">$cjk_node_value</VerticalCallOut> <VerticalText cfg:type="boolean">$cjk_node_value</VerticalText> </CJK> </I18N> EOF; $cjk_node = XML::Twig->new()->parsestring($new_cjk_node)->root; $cjk_node->paste($common_xml->root); $cjk_node = ($cjk_node->descendants("CJK"))[0]; $common_xml_changed = 1; } # FIXME: this seems to be enbled wrt. all children either "true" # or "false", so check value change by picking one. if ($cjk_node->first_child("AsianTypography")->text ne "$cjk_node_value") { $_->set_text("$cjk_node_value") foreach ($cjk_node->descendants()); $common_xml_changed = 1; } # Choose UI fonts according to the current UI language my $font_node = $common_xml->get_xpath("/Common/Font/Substitution/FontPairs", 0); my $new_font_node; if (!$font_node) { $new_font_node = << "EOF;"; <Font> <Substitution> <FontPairs cfg:element-type="FontReplacement"></FontPairs> <Replacement cfg:type="boolean">true</Replacement> </Substitution> </Font> EOF; $font_node = XML::Twig->new()->parsestring($new_font_node)->root; $font_node->paste($common_xml->root); $font_node = ($font_node->descendants("FontPairs"))[0]; } my $override_font = $OOfficeConfig{UI_FONT}; if ($override_font eq "AUTO") { $override_font = ""; my $font = FontOfLanguage $oolang; if ($oolang ne $old_oolang) { # Set new font UI, following the first switch to current language $override_font = $font; } elsif ($new_font_node) { # We had to insert a new <Font> node, thusly also need to # insert default font substitutions $override_font = $font; } } if ($override_font) { foreach my $font ("Andale Sans UI", "Albany", "Arial") { my $font_repl_node = $font_node->get_xpath("./FontReplacement/ReplaceFont[string()=\"$font\"]", 0); if (!$font_repl_node) { # If available, don't substitute any font besides # Andale Sans UI my $always = $font eq "Andale Sans UI" ? "true" : "false"; (my $font_id = $font) =~ s/[-\s]/_/g; my $new_font_repl_node = << "EOF;"; <FontReplacement state="replaced" cfg:name="_ID_$font_id"> <Always cfg:type="boolean">$always</Always> <OnScreenOnly cfg:type="boolean">true</OnScreenOnly> <ReplaceFont cfg:type="string">$font</ReplaceFont> <SubstituteFont cfg:type="string">$override_font</SubstituteFont> </FontReplacement> EOF; $font_repl_node = XML::Twig->new()->parsestring($new_font_repl_node)->root; $font_repl_node->paste("last_child", $font_node); $common_xml_changed = 1; } elsif (($font_repl_node = $font_repl_node->parent()->first_child("SubstituteFont")) && $override_font ne $font_repl_node->text) { $font_repl_node->set_text("$override_font"); $common_xml_changed = 1; } } } # If FONT_SCALING is not set, determine UI font scaling based on # current display resolution. Default to 100 otherwise. my $scale = 100; my $override_fontscaling = $OOfficeConfig{FONT_SCALING}; if ($override_fontscaling =~ /^([0-9]+)$/) { $scale = $1; } elsif ($override_fontscaling eq "AUTO") { $scale = FontScalingFromResolution; } my $scale_node; if (! ($scale_node = ($common_xml->get_xpath("/Common/View/FontScaling"))[0]) ) { # Glue in new <View> section my $new_view_node = << "EOF;"; <View> <FontAntiAliasing> <MinPixelHeight cfg:type="short">8</MinPixelHeight> </FontAntiAliasing> <FontScaling cfg:type="short">$scale</FontScaling> </View> EOF; $scale_node = XML::Twig->new()->parsestring($new_view_node)->root; $scale_node->paste($common_xml->root); $scale_node = ($scale_node->descendants("FontScaling"))[0]; # First time installation, the node doesn't exist $common_xml_changed = 1; } if ($override_fontscaling && $scale ne $scale_node->text) { $scale_node->set_text("$scale"); $common_xml_changed = 1; } OutputXML "$CommonXML", $common_xml if ($common_xml_changed); # 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 configure in the meantime. output "$PsprintConf", ( cat_("$SystemPsprintConf"), "\n" ) if ( TimeStamp "$SystemPsprintConf" > TimeStamp "$PsprintConf" || ! grep /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" ) { # psprint.conf happens to be a "Windows"-style file update_gnomekderc $PsprintConf, $category, PPD_PageSize => $paper_size; } } } # In some cases, OOo requests language from environment variables and # first checks for LANG. Override it thusly. # $ENV{LANG} = "$lang"; # And here we go. ;-) exec "$UserInstallDir/soffice", @ooo_argv if (!$Debug); # Local variables: # tab-width: 4 # indent-tabs-mode: nil # End: