OpenOffice.org ODF, Python and XML
OpenOffice.org Writer showed what we expected. Both the double hyphen and the en dash changed into em dashes, and the starting double quote curves the right way.
Now, here's the rest. The expression to deal with the ending double quote is the mirror image of the starting double quote. In order to write an ending/closing double quote, we require the quote character either to be at the end of the string (\Z) or followed by whitespace. Again, when we do the replacement, we want to replace only the quote itself, not the whitespace. Hence, the Ending Double-quote PATtern (eDpat) is given by:
eDpat = re.compile(r'("\Z)|("(?=\s))', re.U)
By the way, we compile all these patterns because we're going to use them over and over again when processing documents.
To handle single quotes ('like these'), we basically can do the same thing, except for a couple of issues. First, is the problem of contractions. When handling a double-quote character, we didn't cover the case where it was surrounded on both sides by non-whitespace. With single quotes (or apostrophes), we can't avoid that, because of words such as can't. Therefore, although the starting single-quote pattern can match the starting double-quote pattern, the other one, which doubles as an apostrophe in contractions, has a looser pattern. Here's what I came up with:
eSpat = re.compile(r"(?<=\S)'", re.U)
Because the pattern has an apostrophe in it, we delimit the pattern string using double-quote characters. This expression matches a single quote, but only when preceded immediately by a non-whitespace character.
The second issue, which the code doesn't address, is that of contractions beginning with an apostrophe, such as 'tis the season or stick 'em up.
The script treats the leading apostrophe like the start of a single-quoted phrase, and the single quote will face the wrong way. This probably will need a manual work-around.
Putting all this together, we have fix4.py:
#!/usr/bin/python -tt import xml.dom.minidom import sys import re # new in fix3.py DEBUG = 1 def dprint(what): if DEBUG == 0: return sys.stderr.write(what.encode('ascii', 'replace') + '\n') emDash=u'\u2014' enDash=u'\u2013' # new in fix3.py sDquote=u'\u201c' # new in fix3.py eDquote=u'\u201d' # new in fix4.py sSquote=u'\u2018' # new in fix4.py eSquote=u'\u2019' # new in fix4.py # sDpat: pattern for starting dbl quote, as # "Go! <-- the quote there # We look for it either at start (\A) or # after a space (\s), and we want it to be # followed by a non-space sDpat = re.compile(r'(\A|(?<=\s))"(?=\S)', re.U) # new in fix3.py eDpat = re.compile(r'("\Z)|("(?=\s))', re.U) # new in fix4.py sSpat = re.compile(r"(\A|(?<=\s))'(?=\S)", re.U) # new in fix4.py eSpat = re.compile(r"(?<=\S)'", re.U) # new in fix4.py def fixdata(td, depth): dprint("depth=%d: childNode: %s" % (depth, td.data)) # OK, so '--' becomes em dash everywhere td.data = td.data.replace('--', emDash) # Change 'en' dash to 'em' dash td.data = td.data.replace(enDash , emDash) # new in fix3.py # Make a nice starting curly-quote # new in fix3.py td.data = sDpat.sub(sDquote, td.data) # new in fix3.py td.data = eDpat.sub(eDquote, td.data) # new in fix4.py # Make nice curly single-quote characters td.data = sSpat.sub(sSquote, td.data) # new in fix4.py td.data = eSpat.sub(eSquote, td.data) # new in fix4.py def handle_xml_tree(aNode, depth): if aNode.hasChildNodes(): for kid in aNode.childNodes: handle_xml_tree(kid, depth+1) else: if 'data' in dir(aNode): fixdata(aNode, depth) def doit(argv): doc = xml.dom.minidom.parse(argv) handle_xml_tree(doc, 0) sys.stdout.write(doc.toxml('utf-8')) if __name__ == "__main__": doit(sys.argv)
Let's try that on our example:
% ln -sf fix4.py fixit.py % ./fixit.sh ex3.odt ex3-4.odt depth=5: childNode: The ?en? dash ? is too short depth=5: childNode: The ?em? dash ? which is longer ? is what we need. depth=5: childNode: And two hyphens -- ugly -- should be turned into ?em? dashes. depth=5: childNode: This line has "straight double quotes" depth=5: childNode: These 'single quotes' aren't pretty. % oowriter ex3-4.odt
Let's review what we've done here:
Wrote scripts to unpack and repack ODF files.
Learned about using Python to understand the structure of ODF files.
Wrote a Python program to perform useful transformations on an OpenOffice.org Writer file, using regular expressions and the built-in string methods.
Fast/Flexible Linux OS Recovery
On Demand Now
In this live one-hour webinar, learn how to enhance your existing backup strategies for complete disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible full-system recovery solution for UNIX and Linux systems.
Join Linux Journal's Shawn Powers and David Huffman, President/CEO, Storix, Inc.
Free to Linux Journal readers.Register Now!
- Download "Linux Management with Red Hat Satellite: Measuring Business Impact and ROI"
- Peppermint 7 Released
- Sony Settles in Linux Battle
- Libarchive Security Flaw Discovered
- Client-Side Performance
- Maru OS Brings Debian to Your Phone
- Profiles and RC Files
- Snappy Moves to New Platforms
- The Giant Zero, Part 0.x
- Git 2.9 Released
With all the industry talk about the benefits of Linux on Power and all the performance advantages offered by its open architecture, you may be considering a move in that direction. If you are thinking about analytics, big data and cloud computing, you would be right to evaluate Power. The idea of using commodity x86 hardware and replacing it every three years is an outdated cost model. It doesn’t consider the total cost of ownership, and it doesn’t consider the advantage of real processing power, high-availability and multithreading like a demon.
This ebook takes a look at some of the practical applications of the Linux on Power platform and ways you might bring all the performance power of this open architecture to bear for your organization. There are no smoke and mirrors here—just hard, cold, empirical evidence provided by independent sources. I also consider some innovative ways Linux on Power will be used in the future.Get the Guide