MediaWiki  REL1_22
cssjanus.py
Go to the documentation of this file.
00001 #!/usr/bin/python
00002 #
00003 # Copyright 2008 Google Inc. All Rights Reserved.
00004 
00005 """Converts a LeftToRight Cascading Style Sheet into a RightToLeft one.
00006 
00007    This is a utility script for replacing "left" oriented things in a CSS file
00008    like float, padding, margin with "right" oriented values.
00009    It also does the opposite.
00010    The goal is to be able to conditionally serve one large, cat'd, compiled CSS
00011    file appropriate for LeftToRight oriented languages and RightToLeft ones.
00012    This utility will hopefully help your structural layout done in CSS in
00013    terms of its RTL compatibility. It will not help with some of the more
00014    complicated bidirectional text issues.
00015 """
00016 
00017 __author__ = '[email protected] (Lindsey Simon)'
00018 __version__ = '0.1'
00019 
00020 import logging
00021 import re
00022 import sys
00023 import getopt
00024 import os
00025 
00026 import csslex
00027 
00028 logging.getLogger().setLevel(logging.INFO)
00029 
00030 # Global for the command line flags.
00031 SWAP_LTR_RTL_IN_URL_DEFAULT = False
00032 SWAP_LEFT_RIGHT_IN_URL_DEFAULT = False
00033 FLAGS = {'swap_ltr_rtl_in_url': SWAP_LTR_RTL_IN_URL_DEFAULT,
00034          'swap_left_right_in_url': SWAP_LEFT_RIGHT_IN_URL_DEFAULT}
00035 
00036 # Generic token delimiter character.
00037 TOKEN_DELIMITER = '~'
00038 
00039 # This is a temporary match token we use when swapping strings.
00040 TMP_TOKEN = '%sTMP%s' % (TOKEN_DELIMITER, TOKEN_DELIMITER)
00041 
00042 # Token to be used for joining lines.
00043 TOKEN_LINES = '%sJ%s' % (TOKEN_DELIMITER, TOKEN_DELIMITER)
00044 
00045 # Global constant text strings for CSS value matches.
00046 LTR = 'ltr'
00047 RTL = 'rtl'
00048 LEFT = 'left'
00049 RIGHT = 'right'
00050 
00051 # This is a lookbehind match to ensure that we don't replace instances
00052 # of our string token (left, rtl, etc...) if there's a letter in front of it.
00053 # Specifically, this prevents replacements like 'background: url(bright.png)'.
00054 LOOKBEHIND_NOT_LETTER = r'(?<![a-zA-Z])'
00055 
00056 # This is a lookahead match to make sure we don't replace left and right
00057 # in actual classnames, so that we don't break the HTML/CSS dependencies.
00058 # Read literally, it says ignore cases where the word left, for instance, is
00059 # directly followed by valid classname characters and a curly brace.
00060 # ex: .column-left {float: left} will become .column-left {float: right}
00061 LOOKAHEAD_NOT_OPEN_BRACE = (r'(?!(?:%s|%s|%s|#|\:|\.|\,|\+|>)*?{)' %
00062                             (csslex.NMCHAR, TOKEN_LINES, csslex.SPACE))
00063 
00064 
00065 # These two lookaheads are to test whether or not we are within a
00066 # background: url(HERE) situation.
00067 # Ref: http://www.w3.org/TR/CSS21/syndata.html#uri
00068 VALID_AFTER_URI_CHARS = r'[\'\"]?%s' % csslex.WHITESPACE
00069 LOOKAHEAD_NOT_CLOSING_PAREN = r'(?!%s?%s\))' % (csslex.URL_CHARS,
00070                                                 VALID_AFTER_URI_CHARS)
00071 LOOKAHEAD_FOR_CLOSING_PAREN = r'(?=%s?%s\))' % (csslex.URL_CHARS,
00072                                                 VALID_AFTER_URI_CHARS)
00073 
00074 # Compile a regex to swap left and right values in 4 part notations.
00075 # We need to match negatives and decimal numeric values.
00076 # ex. 'margin: .25em -2px 3px 0' becomes 'margin: .25em 0 3px -2px'.
00077 POSSIBLY_NEGATIVE_QUANTITY = r'((?:-?%s)|(?:inherit|auto))' % csslex.QUANTITY
00078 POSSIBLY_NEGATIVE_QUANTITY_SPACE = r'%s%s%s' % (POSSIBLY_NEGATIVE_QUANTITY,
00079                                                 csslex.SPACE,
00080                                                 csslex.WHITESPACE)
00081 FOUR_NOTATION_QUANTITY_RE = re.compile(r'%s%s%s%s' %
00082                                        (POSSIBLY_NEGATIVE_QUANTITY_SPACE,
00083                                         POSSIBLY_NEGATIVE_QUANTITY_SPACE,
00084                                         POSSIBLY_NEGATIVE_QUANTITY_SPACE,
00085                                         POSSIBLY_NEGATIVE_QUANTITY),
00086                                        re.I)
00087 COLOR = r'(%s|%s)' % (csslex.NAME, csslex.HASH)
00088 COLOR_SPACE = r'%s%s' % (COLOR, csslex.SPACE)
00089 FOUR_NOTATION_COLOR_RE = re.compile(r'(-color%s:%s)%s%s%s(%s)' %
00090                                     (csslex.WHITESPACE,
00091                                      csslex.WHITESPACE,
00092                                      COLOR_SPACE,
00093                                      COLOR_SPACE,
00094                                      COLOR_SPACE,
00095                                      COLOR),
00096                                     re.I)
00097 
00098 # Compile the cursor resize regexes
00099 CURSOR_EAST_RE = re.compile(LOOKBEHIND_NOT_LETTER + '([ns]?)e-resize')
00100 CURSOR_WEST_RE = re.compile(LOOKBEHIND_NOT_LETTER + '([ns]?)w-resize')
00101 
00102 # Matches the condition where we need to replace the horizontal component
00103 # of a background-position value when expressed in horizontal percentage.
00104 # Had to make two regexes because in the case of position-x there is only
00105 # one quantity, and otherwise we don't want to match and change cases with only
00106 # one quantity.
00107 BG_HORIZONTAL_PERCENTAGE_RE = re.compile(r'background(-position)?(%s:%s)'
00108                                          '([^%%]*?)(%s)%%'
00109                                          '(%s(?:%s|%s))' % (csslex.WHITESPACE,
00110                                                             csslex.WHITESPACE,
00111                                                             csslex.NUM,
00112                                                             csslex.WHITESPACE,
00113                                                             csslex.QUANTITY,
00114                                                             csslex.IDENT))
00115 
00116 BG_HORIZONTAL_PERCENTAGE_X_RE = re.compile(r'background-position-x(%s:%s)'
00117                                            '(%s)%%' % (csslex.WHITESPACE,
00118                                                        csslex.WHITESPACE,
00119                                                        csslex.NUM))
00120 
00121 # Matches the opening of a body selector.
00122 BODY_SELECTOR = r'body%s{%s' % (csslex.WHITESPACE, csslex.WHITESPACE)
00123 
00124 # Matches anything up until the closing of a selector.
00125 CHARS_WITHIN_SELECTOR = r'[^\}]*?'
00126 
00127 # Matches the direction property in a selector.
00128 DIRECTION_RE = r'direction%s:%s' % (csslex.WHITESPACE, csslex.WHITESPACE)
00129 
00130 # These allow us to swap "ltr" with "rtl" and vice versa ONLY within the
00131 # body selector and on the same line.
00132 BODY_DIRECTION_LTR_RE = re.compile(r'(%s)(%s)(%s)(ltr)' %
00133                                    (BODY_SELECTOR, CHARS_WITHIN_SELECTOR,
00134                                     DIRECTION_RE),
00135                                    re.I)
00136 BODY_DIRECTION_RTL_RE = re.compile(r'(%s)(%s)(%s)(rtl)' %
00137                                    (BODY_SELECTOR, CHARS_WITHIN_SELECTOR,
00138                                     DIRECTION_RE),
00139                                    re.I)
00140 
00141 
00142 # Allows us to swap "direction:ltr" with "direction:rtl" and
00143 # vice versa anywhere in a line.
00144 DIRECTION_LTR_RE = re.compile(r'%s(ltr)' % DIRECTION_RE)
00145 DIRECTION_RTL_RE = re.compile(r'%s(rtl)' % DIRECTION_RE)
00146 
00147 # We want to be able to switch left with right and vice versa anywhere
00148 # we encounter left/right strings, EXCEPT inside the background:url(). The next
00149 # two regexes are for that purpose. We have alternate IN_URL versions of the
00150 # regexes compiled in case the user passes the flag that they do
00151 # actually want to have left and right swapped inside of background:urls.
00152 LEFT_RE = re.compile('%s(%s)%s%s' % (LOOKBEHIND_NOT_LETTER,
00153                                      LEFT,
00154                                      LOOKAHEAD_NOT_CLOSING_PAREN,
00155                                      LOOKAHEAD_NOT_OPEN_BRACE),
00156                      re.I)
00157 RIGHT_RE = re.compile('%s(%s)%s%s' % (LOOKBEHIND_NOT_LETTER,
00158                                       RIGHT,
00159                                       LOOKAHEAD_NOT_CLOSING_PAREN,
00160                                       LOOKAHEAD_NOT_OPEN_BRACE),
00161                       re.I)
00162 LEFT_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER,
00163                                           LEFT,
00164                                           LOOKAHEAD_FOR_CLOSING_PAREN),
00165                             re.I)
00166 RIGHT_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER,
00167                                            RIGHT,
00168                                            LOOKAHEAD_FOR_CLOSING_PAREN),
00169                              re.I)
00170 LTR_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER,
00171                                          LTR,
00172                                          LOOKAHEAD_FOR_CLOSING_PAREN),
00173                            re.I)
00174 RTL_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER,
00175                                          RTL,
00176                                          LOOKAHEAD_FOR_CLOSING_PAREN),
00177                            re.I)
00178 
00179 COMMENT_RE = re.compile('(%s)' % csslex.COMMENT, re.I)
00180 
00181 NOFLIP_TOKEN = r'\@noflip'
00182 # The NOFLIP_TOKEN inside of a comment. For now, this requires that comments
00183 # be in the input, which means users of a css compiler would have to run
00184 # this script first if they want this functionality.
00185 NOFLIP_ANNOTATION = r'/\*%s%s%s\*/' % (csslex.WHITESPACE,
00186                                        NOFLIP_TOKEN,
00187                                        csslex. WHITESPACE)
00188 
00189 # After a NOFLIP_ANNOTATION, and within a class selector, we want to be able
00190 # to set aside a single rule not to be flipped. We can do this by matching
00191 # our NOFLIP annotation and then using a lookahead to make sure there is not
00192 # an opening brace before the match.
00193 NOFLIP_SINGLE_RE = re.compile(r'(%s%s[^;}]+;?)' % (NOFLIP_ANNOTATION,
00194                                                    LOOKAHEAD_NOT_OPEN_BRACE),
00195                               re.I)
00196 
00197 # After a NOFLIP_ANNOTATION, we want to grab anything up until the next } which
00198 # means the entire following class block. This will prevent all of its
00199 # declarations from being flipped.
00200 NOFLIP_CLASS_RE = re.compile(r'(%s%s})' % (NOFLIP_ANNOTATION,
00201                                            CHARS_WITHIN_SELECTOR),
00202                              re.I)
00203 
00204 
00205 class Tokenizer:
00206   """Replaces any CSS comments with string tokens and vice versa."""
00207 
00208   def __init__(self, token_re, token_string):
00209     """Constructor for the Tokenizer.
00210 
00211     Args:
00212       token_re: A regex for the string to be replace by a token.
00213       token_string: The string to put between token delimiters when tokenizing.
00214     """
00215     logging.debug('Tokenizer::init token_string=%s' % token_string)
00216     self.token_re = token_re
00217     self.token_string = token_string
00218     self.originals = []
00219 
00220   def Tokenize(self, line):
00221     """Replaces any string matching token_re in line with string tokens.
00222 
00223     By passing a function as an argument to the re.sub line below, we bypass
00224     the usual rule where re.sub will only replace the left-most occurrence of
00225     a match by calling the passed in function for each occurrence.
00226 
00227     Args:
00228       line: A line to replace token_re matches in.
00229 
00230     Returns:
00231       line: A line with token_re matches tokenized.
00232     """
00233     line = self.token_re.sub(self.TokenizeMatches, line)
00234     logging.debug('Tokenizer::Tokenize returns: %s' % line)
00235     return line
00236 
00237   def DeTokenize(self, line):
00238     """Replaces tokens with the original string.
00239 
00240     Args:
00241       line: A line with tokens.
00242 
00243     Returns:
00244       line with any tokens replaced by the original string.
00245     """
00246 
00247     # Put all of the comments back in by their comment token.
00248     for i, original in enumerate(self.originals):
00249       token = '%s%s_%s%s' % (TOKEN_DELIMITER, self.token_string, i + 1,
00250                              TOKEN_DELIMITER)
00251       line = line.replace(token, original)
00252       logging.debug('Tokenizer::DeTokenize i:%s w/%s' % (i, token))
00253     logging.debug('Tokenizer::DeTokenize returns: %s' % line)
00254     return line
00255 
00256   def TokenizeMatches(self, m):
00257     """Replaces matches with tokens and stores the originals.
00258 
00259     Args:
00260       m: A match object.
00261 
00262     Returns:
00263       A string token which replaces the CSS comment.
00264     """
00265     logging.debug('Tokenizer::TokenizeMatches %s' % m.group(1))
00266     self.originals.append(m.group(1))
00267     return '%s%s_%s%s' % (TOKEN_DELIMITER,
00268                           self.token_string,
00269                           len(self.originals),
00270                           TOKEN_DELIMITER)
00271 
00272 
00273 def FixBodyDirectionLtrAndRtl(line):
00274   """Replaces ltr with rtl and vice versa ONLY in the body direction.
00275 
00276   Args:
00277     line: A string to replace instances of ltr with rtl.
00278   Returns:
00279     line with direction: ltr and direction: rtl swapped only in body selector.
00280     line = FixBodyDirectionLtrAndRtl('body { direction:ltr }')
00281     line will now be 'body { direction:rtl }'.
00282   """
00283 
00284   line = BODY_DIRECTION_LTR_RE.sub('\\1\\2\\3%s' % TMP_TOKEN, line)
00285   line = BODY_DIRECTION_RTL_RE.sub('\\1\\2\\3%s' % LTR, line)
00286   line = line.replace(TMP_TOKEN, RTL)
00287   logging.debug('FixBodyDirectionLtrAndRtl returns: %s' % line)
00288   return line
00289 
00290 
00291 def FixLeftAndRight(line):
00292   """Replaces left with right and vice versa in line.
00293 
00294   Args:
00295     line: A string in which to perform the replacement.
00296 
00297   Returns:
00298     line with left and right swapped. For example:
00299     line = FixLeftAndRight('padding-left: 2px; margin-right: 1px;')
00300     line will now be 'padding-right: 2px; margin-left: 1px;'.
00301   """
00302 
00303   line = LEFT_RE.sub(TMP_TOKEN, line)
00304   line = RIGHT_RE.sub(LEFT, line)
00305   line = line.replace(TMP_TOKEN, RIGHT)
00306   logging.debug('FixLeftAndRight returns: %s' % line)
00307   return line
00308 
00309 
00310 def FixLeftAndRightInUrl(line):
00311   """Replaces left with right and vice versa ONLY within background urls.
00312 
00313   Args:
00314     line: A string in which to replace left with right and vice versa.
00315 
00316   Returns:
00317     line with left and right swapped in the url string. For example:
00318     line = FixLeftAndRightInUrl('background:url(right.png)')
00319     line will now be 'background:url(left.png)'.
00320   """
00321 
00322   line = LEFT_IN_URL_RE.sub(TMP_TOKEN, line)
00323   line = RIGHT_IN_URL_RE.sub(LEFT, line)
00324   line = line.replace(TMP_TOKEN, RIGHT)
00325   logging.debug('FixLeftAndRightInUrl returns: %s' % line)
00326   return line
00327 
00328 
00329 def FixLtrAndRtlInUrl(line):
00330   """Replaces ltr with rtl and vice versa ONLY within background urls.
00331 
00332   Args:
00333     line: A string in which to replace ltr with rtl and vice versa.
00334 
00335   Returns:
00336     line with left and right swapped. For example:
00337     line = FixLtrAndRtlInUrl('background:url(rtl.png)')
00338     line will now be 'background:url(ltr.png)'.
00339   """
00340 
00341   line = LTR_IN_URL_RE.sub(TMP_TOKEN, line)
00342   line = RTL_IN_URL_RE.sub(LTR, line)
00343   line = line.replace(TMP_TOKEN, RTL)
00344   logging.debug('FixLtrAndRtlInUrl returns: %s' % line)
00345   return line
00346 
00347 
00348 def FixCursorProperties(line):
00349   """Fixes directional CSS cursor properties.
00350 
00351   Args:
00352     line: A string to fix CSS cursor properties in.
00353 
00354   Returns:
00355     line reformatted with the cursor properties substituted. For example:
00356     line = FixCursorProperties('cursor: ne-resize')
00357     line will now be 'cursor: nw-resize'.
00358   """
00359 
00360   line = CURSOR_EAST_RE.sub('\\1' + TMP_TOKEN, line)
00361   line = CURSOR_WEST_RE.sub('\\1e-resize', line)
00362   line = line.replace(TMP_TOKEN, 'w-resize')
00363   logging.debug('FixCursorProperties returns: %s' % line)
00364   return line
00365 
00366 
00367 def FixFourPartNotation(line):
00368   """Fixes the second and fourth positions in 4 part CSS notation.
00369 
00370   Args:
00371     line: A string to fix 4 part CSS notation in.
00372 
00373   Returns:
00374     line reformatted with the 4 part notations swapped. For example:
00375     line = FixFourPartNotation('padding: 1px 2px 3px 4px')
00376     line will now be 'padding: 1px 4px 3px 2px'.
00377   """
00378   line = FOUR_NOTATION_QUANTITY_RE.sub('\\1 \\4 \\3 \\2', line)
00379   line = FOUR_NOTATION_COLOR_RE.sub('\\1\\2 \\5 \\4 \\3', line)
00380   logging.debug('FixFourPartNotation returns: %s' % line)
00381   return line
00382 
00383 
00384 def FixBackgroundPosition(line):
00385   """Fixes horizontal background percentage values in line.
00386 
00387   Args:
00388     line: A string to fix horizontal background position values in.
00389 
00390   Returns:
00391     line reformatted with the 4 part notations swapped.
00392   """
00393   line = BG_HORIZONTAL_PERCENTAGE_RE.sub(CalculateNewBackgroundPosition, line)
00394   line = BG_HORIZONTAL_PERCENTAGE_X_RE.sub(CalculateNewBackgroundPositionX,
00395                                            line)
00396   logging.debug('FixBackgroundPosition returns: %s' % line)
00397   return line
00398 
00399 
00400 def CalculateNewBackgroundPosition(m):
00401   """Fixes horizontal background-position percentages.
00402 
00403   This function should be used as an argument to re.sub since it needs to
00404   perform replacement specific calculations.
00405 
00406   Args:
00407     m: A match object.
00408 
00409   Returns:
00410     A string with the horizontal background position percentage fixed.
00411     BG_HORIZONTAL_PERCENTAGE_RE.sub(FixBackgroundPosition,
00412                                     'background-position: 75% 50%')
00413     will return 'background-position: 25% 50%'.
00414   """
00415 
00416   # The flipped value is the offset from 100%
00417   new_x = str(100-int(m.group(4)))
00418 
00419   # Since m.group(1) may very well be None type and we need a string..
00420   if m.group(1):
00421     position_string = m.group(1)
00422   else:
00423     position_string = ''
00424 
00425   return 'background%s%s%s%s%%%s' % (position_string, m.group(2), m.group(3),
00426                                      new_x, m.group(5))
00427 
00428 
00429 def CalculateNewBackgroundPositionX(m):
00430   """Fixes percent based background-position-x.
00431 
00432   This function should be used as an argument to re.sub since it needs to
00433   perform replacement specific calculations.
00434 
00435   Args:
00436     m: A match object.
00437 
00438   Returns:
00439     A string with the background-position-x percentage fixed.
00440     BG_HORIZONTAL_PERCENTAGE_X_RE.sub(CalculateNewBackgroundPosition,
00441                                       'background-position-x: 75%')
00442     will return 'background-position-x: 25%'.
00443   """
00444 
00445   # The flipped value is the offset from 100%
00446   new_x = str(100-int(m.group(2)))
00447 
00448   return 'background-position-x%s%s%%' % (m.group(1), new_x)
00449 
00450 
00451 def ChangeLeftToRightToLeft(lines,
00452                             swap_ltr_rtl_in_url=None,
00453                             swap_left_right_in_url=None):
00454   """Turns lines into a stream and runs the fixing functions against it.
00455 
00456   Args:
00457     lines: An list of CSS lines.
00458     swap_ltr_rtl_in_url: Overrides this flag if param is set.
00459     swap_left_right_in_url: Overrides this flag if param is set.
00460 
00461   Returns:
00462     The same lines, but with left and right fixes.
00463   """
00464 
00465   global FLAGS
00466 
00467   # Possibly override flags with params.
00468   logging.debug('ChangeLeftToRightToLeft swap_ltr_rtl_in_url=%s, '
00469                 'swap_left_right_in_url=%s' % (swap_ltr_rtl_in_url,
00470                                                swap_left_right_in_url))
00471   if swap_ltr_rtl_in_url is None:
00472     swap_ltr_rtl_in_url = FLAGS['swap_ltr_rtl_in_url']
00473   if swap_left_right_in_url is None:
00474     swap_left_right_in_url = FLAGS['swap_left_right_in_url']
00475 
00476   # Turns the array of lines into a single line stream.
00477   logging.debug('LINES COUNT: %s' % len(lines))
00478   line = TOKEN_LINES.join(lines)
00479 
00480   # Tokenize any single line rules with the /* noflip */ annotation.
00481   noflip_single_tokenizer = Tokenizer(NOFLIP_SINGLE_RE, 'NOFLIP_SINGLE')
00482   line = noflip_single_tokenizer.Tokenize(line)
00483 
00484   # Tokenize any class rules with the /* noflip */ annotation.
00485   noflip_class_tokenizer = Tokenizer(NOFLIP_CLASS_RE, 'NOFLIP_CLASS')
00486   line = noflip_class_tokenizer.Tokenize(line)
00487 
00488   # Tokenize the comments so we can preserve them through the changes.
00489   comment_tokenizer = Tokenizer(COMMENT_RE, 'C')
00490   line = comment_tokenizer.Tokenize(line)
00491 
00492   # Here starteth the various left/right orientation fixes.
00493   line = FixBodyDirectionLtrAndRtl(line)
00494 
00495   if swap_left_right_in_url:
00496     line = FixLeftAndRightInUrl(line)
00497 
00498   if swap_ltr_rtl_in_url:
00499     line = FixLtrAndRtlInUrl(line)
00500 
00501   line = FixLeftAndRight(line)
00502   line = FixCursorProperties(line)
00503   line = FixFourPartNotation(line)
00504   line = FixBackgroundPosition(line)
00505 
00506   # DeTokenize the single line noflips.
00507   line = noflip_single_tokenizer.DeTokenize(line)
00508 
00509   # DeTokenize the class-level noflips.
00510   line = noflip_class_tokenizer.DeTokenize(line)
00511 
00512   # DeTokenize the comments.
00513   line = comment_tokenizer.DeTokenize(line)
00514 
00515   # Rejoin the lines back together.
00516   lines = line.split(TOKEN_LINES)
00517 
00518   return lines
00519 
00520 def usage():
00521   """Prints out usage information."""
00522 
00523   print 'Usage:'
00524   print '  ./cssjanus.py < file.css > file-rtl.css'
00525   print 'Flags:'
00526   print '  --swap_left_right_in_url: Fixes "left"/"right" string within urls.'
00527   print '  Ex: ./cssjanus.py --swap_left_right_in_url < file.css > file_rtl.css'
00528   print '  --swap_ltr_rtl_in_url: Fixes "ltr"/"rtl" string within urls.'
00529   print '  Ex: ./cssjanus --swap_ltr_rtl_in_url < file.css > file_rtl.css'
00530 
00531 def setflags(opts):
00532   """Parse the passed in command line arguments and set the FLAGS global.
00533 
00534   Args:
00535     opts: getopt iterable intercepted from argv.
00536   """
00537 
00538   global FLAGS
00539 
00540   # Parse the arguments.
00541   for opt, arg in opts:
00542     logging.debug('opt: %s, arg: %s' % (opt, arg))
00543     if opt in ("-h", "--help"):
00544       usage()
00545       sys.exit()
00546     elif opt in ("-d", "--debug"):
00547       logging.getLogger().setLevel(logging.DEBUG)
00548     elif opt == '--swap_ltr_rtl_in_url':
00549       FLAGS['swap_ltr_rtl_in_url'] = True
00550     elif opt == '--swap_left_right_in_url':
00551       FLAGS['swap_left_right_in_url'] = True
00552 
00553 
00554 def main(argv):
00555   """Sends stdin lines to ChangeLeftToRightToLeft and writes to stdout."""
00556 
00557   # Define the flags.
00558   try:
00559     opts, args = getopt.getopt(argv, 'hd', ['help', 'debug',
00560                                             'swap_left_right_in_url',
00561                                             'swap_ltr_rtl_in_url'])
00562   except getopt.GetoptError:
00563     usage()
00564     sys.exit(2)
00565 
00566   # Parse and set the flags.
00567   setflags(opts)
00568 
00569   # Call the main routine with all our functionality.
00570   fixed_lines = ChangeLeftToRightToLeft(sys.stdin.readlines())
00571   sys.stdout.write(''.join(fixed_lines))
00572 
00573 if __name__ == '__main__':
00574   main(sys.argv[1:])