MediaWiki
REL1_22
|
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:])