diff -u -N KindleUnpack_v072a/lib//kindleunpack.py KindleUnpack_v072b/lib//kindleunpack.py
--- KindleUnpack_v072a/lib//kindleunpack.py	2014-06-18 15:08:20 +0900
+++ KindleUnpack_v072b/lib//kindleunpack.py	2014-06-22 23:18:38 +0900
@@ -94,6 +94,7 @@
 #  0.71 - extensive refactoring of kindleunpack.py to make it more manageable
 #  0.72 - many bug fixes from tkeo: fix pageProcessing, fix print replica, fix resc usage, fix font mangling, etc.
 #  0.72a- fix for still broken PrintReplica support
+#  0.72b- preview for primary epub3 support. A parameter epubver(default='2') is added to process_all_mobi_headers(), unpackBook().
 
 DUMP = False
 """ Set to True to dump all possible information. """
@@ -104,9 +105,6 @@
 SPLIT_COMBO_MOBIS = False
 """ Set to True to split combination mobis into mobi7 and mobi8 pieces. """
 
-PROC_K8RESC = True
-""" Process K8 RESC section. """
-
 CREATE_COVER_PAGE = True # XXX experimental
 """ Create and insert a cover xhtml page. """
 
@@ -155,30 +153,13 @@
 from mobi_ncx import ncxExtract
 from mobi_k8proc import K8Processor
 from mobi_split import mobi_split
-from mobi_k8resc import K8RESCProcessor, CoverProcessor
+from mobi_k8resc import K8RESCProcessor
+from mobi_nav import NAVProcessor
+from mobi_cover import CoverProcessor
 from mobi_pagemap import PageMapProcessor
 from mobi_dict import dictSupport
 
 
-def renameCoverImage(metadata, files, imgnames):
-    global CREATE_COVER_PAGE
-    if CREATE_COVER_PAGE:
-        if 'CoverOffset' in metadata.keys():
-            i = int(metadata['CoverOffset'][0])
-            if imgnames[i] != None:
-                old_name = imgnames[i]
-                if not 'cover' in old_name:
-                    new_name = re.sub(r'image', 'cover', old_name)
-                    imgnames[i] = new_name
-                    oldimg = os.path.join(files.imgdir, old_name)
-                    newimg = os.path.join(files.imgdir, new_name)
-                    if os.path.exists(pathof(oldimg)):
-                        if os.path.exists(pathof(newimg)):
-                            os.remove(pathof(newimg))
-                        os.rename(pathof(oldimg), pathof(newimg))
-    return imgnames
-
-
 def processSRCS(i, files, imgnames, sect, data):
     # extract the source zip archive and save it.
     print "File contains kindlegen source archive, extracting as %s" % KINDLEGENSRC_FILENAME
@@ -220,7 +201,7 @@
     fontname = "font%05d" % i
     ext = '.dat'
     font_error = False
-    font_data = data 
+    font_data = data
     try:
         usize, fflags, dstart, xor_len, xor_start = struct.unpack_from('>LLLLL',data,4)
     except:
@@ -260,7 +241,7 @@
     return imgnames, obfuscate_data
 
 
-def processCRES(i, files, imgnames, sect, data):
+def processCRES(i, files, imgnames, sect, data, cover_index=-1):
     # extract an HDImage
     global DUMP
     data = data[12:]
@@ -289,6 +270,8 @@
             sect.setsectiondescription(i,"Mysterious CRES data, first four bytes %s extracting as %s" % (describe(data[0:4]), fname))
     else:
         imgname = "HDimage%05d.%s" % (i, imgtype)
+        if i == cover_index:
+            imgname = "HDcover%05d.%s" % (i, imgtype)
         print "Extracting HD image: {0:s} from section {1:d}".format(imgname,i)
         outimg = os.path.join(files.hdimgdir, imgname)
         open(pathof(outimg), 'wb').write(data)
@@ -298,7 +281,7 @@
     return imgnames
 
 
-def processCONT(i, files, imgnames, sect, data):
+def processCONT(i, files, imgnames, sect, data, begCONT=-1):
     global DUMP
     # process a container header, most of this is unknown
     # right now only extract its EXTH
@@ -317,7 +300,9 @@
             fname = "CONT_Header%05d.dat" % i
             outname= os.path.join(files.outdir, fname)
             open(pathof(outname), 'wb').write(data)
-    return imgnames
+        if begCONT < 0:
+            begCONT = i + 1
+    return imgnames, begCONT
 
 
 def processkind(i, files, imgnames, sect, data):
@@ -380,7 +365,7 @@
     return imgnames, resc
 
 
-def processImage(i, files, imgnames, sect, data):
+def processImage(i, files, imgnames, sect, data, cover_index=-1):
     global DUMP
     # Extract an Image
 
@@ -410,6 +395,8 @@
             sect.setsectiondescription(i,"Mysterious Section, first four bytes %s extracting as %s" % (describe(data[0:4]), fname))
     else:
         imgname = "image%05d.%s" % (i, imgtype)
+        if i == cover_index:
+            imgname = "cover%05d.%s" % (i, imgtype)
         print "Extracting image: {0:s} from section {1:d}".format(imgname,i)
         outimg = os.path.join(files.imgdir, imgname)
         open(pathof(outimg), 'wb').write(data)
@@ -457,7 +444,7 @@
 
 
 
-def processMobi8(mh, metadata, sect, files, imgnames, pagemapproc, resc, obfuscate_data, apnxfile=None):
+def processMobi8(mh, metadata, sect, files, imgnames, pagemapproc, resc, obfuscate_data, apnxfile=None, epubver='2'):
     global DUMP
     global WRITE_RAW_DATA
 
@@ -493,7 +480,7 @@
             linktgt += '#' + idtext
         guidetext += '<reference type="text" href="Text/%s" />\n' % linktgt
 
-    # if apnxfile is passed in use it for page map information 
+    # if apnxfile is passed in use it for page map information
     if apnxfile != None and pagemapproc == None:
         apnxdata = "00000000" + file(apnxfile, 'rb').read()
         pagemapproc = PageMapProcessor(mh, apnxdata)
@@ -548,66 +535,55 @@
             fname = os.path.join(files.k8oebps,dir,filename)
             open(pathof(fname),'wb').write(flowpart)
 
-    k8resc = None
 
-    # ****FIXME****:  pass k8proc into mobi_k8resc routines and offload most of the work done here to mobi_k8resc
+    # XXX: offloaded most of the work done here to mobi_k8resc.
     # process RESC spine information
+    try:
+        # Retrieve a spine and a metadata from RESC section.
+        k8resc = K8RESCProcessor(resc, k8proc)
+    except:
+        k8resc = None
 
-    if PROC_K8RESC:
-        # Retrieve a spine (and a metadata) from RESC section.
-        # Get correspondences between itemes in a spine in RECS and ones in a skelton.
-        k8resc = K8RESCProcessor(resc)
-        n =  k8proc.getNumberOfParts()
-        if k8resc.spine == None: # make a spine if not able to retrieve from a RESC section.
-            spine = [['<spine toc="ncx">', None, None, True, None]]
-            for i in range(n):
-                spine.append(['<itemref/>', i, 'skel{:d}'.format(i), False, None])
-            spine.append(['</spine>', None, None, True, None])
-            k8resc.spine = spine
-            k8resc.createSkelidToSpineIndexDict()
-        for i in range(n):
-            # Import skelnum and filename to K8RescProcessor.
-            [skelnum, dir, filename, beg, end, aidtext] = k8proc.getPartInfo(i)
-            index = k8resc.getSpineIndexBySkelid(skelnum)
-            k8resc.setFilenameToSpine(index, filename)
-
-        if CREATE_COVER_PAGE:
-            cover = CoverProcessor(files, metadata, imgnames)
-            # Create a cover page if first spine item has no correspondence.
+    if k8resc != None and CREATE_COVER_PAGE:
+        # Create a cover page if not exist.
+        cover = CoverProcessor(files, metadata, imgnames)
+        cover_img = cover.getImageName()
+        if cover_img != None:
             createCoverPage = False
-            index = k8resc.getSpineStartIndex()
-            if k8resc.getSpineSkelid(index) < 0:
+            cover_page = cover.getXHTMLName()
+            first_itemref_index = k8resc.spine.getStartIndex()
+            if not k8resc.spine.isLinkToPart(first_itemref_index):
+                # Create a cover page if first spine item has no correspondence.
                 createCoverPage = True
             else:
-                filename = k8resc.getFilenameFromSpine(index)
-                if filename != None:
-                    fname = os.path.join(files.k8oebps, dir, filename)
-                    f = open(pathof(fname), 'r')
-                    if f != None:
-                        xhtml = f.read()
-                        f.close()
-                        cover_img = cover.getImageName()
-                        if cover_img != None and not cover_img in xhtml:
-                            k8resc.insertSpine(index, 'inserted_cover', -1, None)
-                            k8resc.createSkelidToSpineIndexDict()
-                            createCoverPage = True
+                # Insert a cover page if first spine item does not use the cover image.
+                partno = k8resc.spine.getPartno(first_itemref_index)
+                if partno >= 0 and partno < k8proc.getNumberOfParts():
+                    part = k8proc.getPart(partno)
+                else:
+                    part = ''
+                if not cover_img in part:
+                    k8resc.spine.insert(first_itemref_index, 'inserted_cover')
+                    createCoverPage = True
+
             if createCoverPage:
-                cover.writeXHTML()
-                filename = cover.getXHTMLName()
-                dir = os.path.relpath(files.k8text, files.k8oebps)
-                filenames.append([dir, filename])
-                k8resc.setFilenameToSpine(index, filename)
-                k8resc.setSpineAttribute(index, 'linear', 'no')
+                k8resc.spine.setFilename(first_itemref_index, cover_page)
+                k8resc.spine.setAttribute(first_itemref_index, 'linear', 'no')
+
                 guidetext += cover.guide_toxml()
+                filename = cover.getXHTMLName()
+                filenames.append(['Text', filename])
+                cover.writeXHTML()
 
-        k8resc.createFilenameToSpineIndexDict()
 
     # create the opf
     opf = OPFProcessor(files, metadata, filenames, imgnames, ncx.isNCX, mh, usedmap, pagemapxml, guidetext, k8resc)
-    if obfuscate_data:
-        uuid = opf.writeOPF(True)
-    else:
-        uuid = opf.writeOPF()
+    uuid = opf.writeK8OPF(bool(obfuscate_data), epubver)
+
+    if opf.hasNAV():
+        # Create a navigation document.
+        nav = NAVProcessor(files, metadata)
+        nav.writeNAV(ncx_data, guidetext)
 
     # make an epub-like structure of it all
     files.makeEPUB(usedmap, obfuscate_data, uuid)
@@ -709,7 +685,7 @@
             sect.setsectiondescription(i, description)
 
 
-def process_all_mobi_headers(files, apnxfile, sect, mhlst, K8Boundary, k8only=False):
+def process_all_mobi_headers(files, apnxfile, sect, mhlst, K8Boundary, k8only=False, epubver='2'):
     global DUMP
     global WRITE_RAW_DATA
     imgnames = []
@@ -755,6 +731,10 @@
             # processing first part of a combination file
             end = K8Boundary
 
+        cover_offset = int(metadata.get('CoverOffset', ['-1'])[0])
+        if not CREATE_COVER_PAGE:
+            cover_offset = -1
+        begCONT = -1
         for i in xrange(beg, end):
             data = sect.loadSection(i)
             type = data[0:4]
@@ -780,12 +760,12 @@
             elif type == "FONT":
                 imgnames, obfuscate_data = processFONT(i, files, imgnames, sect, data, obfuscate_data)
             elif type == "CRES":
-                imgnames = processCRES(i, files, imgnames, sect, data)
+                imgnames = processCRES(i, files, imgnames, sect, data, begCONT + cover_offset)
             elif type == "CONT":
-                imgnames = processCONT(i, files, imgnames, sect, data)
+                imgnames, begCONT = processCONT(i, files, imgnames, sect, data, begCONT)
             elif type == "kind":
                 imgnames = processkind(i, files, imgnames, sect, data)
-            elif type == chr(0xa0) + chr(0xa0) + chr(0xa0) + chr(0xa0): 
+            elif type == chr(0xa0) + chr(0xa0) + chr(0xa0) + chr(0xa0):
                 sect.setsectiondescription(i,"Empty_HD_Image/Resource_Placeholder")
                 imgnames.append(None)
             elif type == "RESC":
@@ -798,11 +778,11 @@
                 imgnames.append(None)
             else:
                 # if reached here should be an image ow treat as unknown
-                imgnames = processImage(i, files, imgnames, sect, data)
+                imgnames = processImage(i, files, imgnames, sect, data, beg + cover_offset)
 
         # done unpacking resources
         # rename cover image to 'coverXXXXX.ext'
-        imgnames = renameCoverImage(metadata, files, imgnames)
+        #imgnames = renameCoverImage(metadata, files, imgnames)
 
         # Print Replica
         if mh.isPrintReplica() and not k8only:
@@ -811,7 +791,7 @@
 
         # KF8 (Mobi 8)
         if mh.isK8():
-            processMobi8(mh, metadata, sect, files, imgnames, pagemapproc, resc, obfuscate_data, apnxfile)
+            processMobi8(mh, metadata, sect, files, imgnames, pagemapproc, resc, obfuscate_data, apnxfile, epubver)
 
         # Old Mobi (Mobi 7)
         elif not k8only:
@@ -823,7 +803,7 @@
     return
 
 
-def unpackBook(infile, outdir, apnxfile=None, dodump=False, dowriteraw=False, dosplitcombos=False):
+def unpackBook(infile, outdir, apnxfile=None, epubver='2', dodump=False, dowriteraw=False, dosplitcombos=False):
     global DUMP
     global WRITE_RAW_DATA
     global SPLIT_COMBO_MOBIS
@@ -892,7 +872,7 @@
     if hasK8:
         files.makeK8Struct()
 
-    process_all_mobi_headers(files, apnxfile, sect, mhlst, K8Boundary, False)
+    process_all_mobi_headers(files, apnxfile, sect, mhlst, K8Boundary, False, epubver)
 
     if DUMP:
         sect.dumpsectionsinfo()
@@ -906,20 +886,21 @@
     print "  or an unencrypted Kindle/Print Replica ebook to PDF and images"
     print "  into the specified output folder."
     print "Usage:"
-    print "  %s -r -s -p apnxfile -d -h infile [outdir]" % progname
+    print "  %s -r -s -p apnxfile -d -h --epub_version= infile [outdir]" % progname
     print "Options:"
     print "    -r              write raw data to the output folder"
     print "    -s              split combination mobis into mobi7 and mobi8 ebooks"
     print "    -d              dump headers and other info to output and extra files"
     print "    -h              print this help message"
     print "    -p   apnxfile   apnx file associated with an azw3 infile"
+    print "    --epub_version= set epub version. 2:epub2, 3:epub3, A:define automatically"
 
 
 def main():
     global DUMP
     global WRITE_RAW_DATA
     global SPLIT_COMBO_MOBIS
-    print "kindleunpack v0.72a"
+    print "kindleunpack v0.72b preview for epub3 support"
     print "   Based on initial mobipocket version Copyright © 2009 Charles M. Hannum <root@ihack.net>"
     print "   Extensive Extensions and Improvements Copyright © 2009-2014 "
     print "       by:  P. Durrant, K. Hendricks, S. Siebert, fandrieu, DiapDealer, nickredding, tkeo."
@@ -930,7 +911,7 @@
     argv=utf8_argv()
     progname = os.path.basename(argv[0])
     try:
-        opts, args = getopt.getopt(argv[1:], "hdrsp:")
+        opts, args = getopt.getopt(argv[1:], "hdrsp:", ['epub_version='])
     except getopt.GetoptError, err:
         print str(err)
         usage(progname)
@@ -941,6 +922,7 @@
         sys.exit(2)
 
     apnxfile = None
+    epubver = '2'
 
     for o, a in opts:
         if o == "-d":
@@ -954,6 +936,8 @@
         if o == "-h":
             usage(progname)
             sys.exit(0)
+        if o == "--epub_version":
+            epubver = a
 
     if len(args) > 1:
         infile, outdir = args
@@ -969,7 +953,7 @@
 
     try:
         print 'Unpacking Book...'
-        unpackBook(infile, outdir, apnxfile)
+        unpackBook(infile, outdir, apnxfile, epubver)
         print 'Completed'
 
     except ValueError, e:
diff -u -N KindleUnpack_v072a/lib//mobi_cover.py KindleUnpack_v072b/lib//mobi_cover.py
--- KindleUnpack_v072a/lib//mobi_cover.py	1970-01-01 09:00:00 +0900
+++ KindleUnpack_v072b/lib//mobi_cover.py	2014-06-22 21:25:33 +0900
@@ -0,0 +1,227 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+USE_SVG_WRAPPER = True
+""" Set to True to use svg wrapper for default. """
+
+FORCE_DEFAULT_TITLE = False
+""" Set to True to force to use the default title. """
+
+COVER_PAGE_FINENAME = 'cover_page.xhtml'
+""" The name for the cover page. """
+
+DEFAULT_TITLE = 'Cover'
+""" The default title for the cover page. """
+
+MAX_WIDTH = 4096
+""" The max width for the svg cover page. """
+
+MAX_HIEGHT = 4096
+""" The max height for the svg cover page. """
+
+
+import sys, os, re
+import struct, imghdr
+from path import pathof
+
+
+def get_image_type(imgname, imgdata=None):
+    '''Determine the image type.
+
+    '''
+    imgtype = imghdr.what(imgname, imgdata)
+    # imghdr only checks for JFIF or Exif JPEG files. Apparently, there are some
+    # with only the magic JPEG bytes out there...
+    # ImageMagick handles those, so, do it too.
+    if imgtype is None:
+        if imgdata == None:
+            f = open(imgname, 'rb')
+            imgdata = f.read()
+        if imgdata[0:2] == b'\xFF\xD8':
+            # Get last non-null bytes
+            last = len(imgdata)
+            while (imgdata[last-1:last] == b'\x00'):
+                last-=1
+            # Be extra safe, check the trailing bytes, too.
+            if imgdata[last-2:last] == b'\xFF\xD9':
+                imgtype = "jpeg"
+    return imgtype
+
+
+def get_image_size(imgname, imgdata=None):
+    '''Determine the image type of imgname (or imgdata) and return its size.
+
+    Originally,
+    Determine the image type of fhandle and return its size.
+    from draco'''
+    if imgdata == None:
+        fhandle = open(imgname, 'rb')
+        head = fhandle.read(24)
+    else:
+        head = imgdata[0:24]
+    if len(head) != 24:
+        return
+
+    imgtype = get_image_type(imgname, imgdata)
+    if imgtype == 'png':
+        check = struct.unpack('>i', head[4:8])[0]
+        if check != 0x0d0a1a0a:
+            return
+        width, height = struct.unpack('>ii', head[16:24])
+    elif imgtype == 'gif':
+        width, height = struct.unpack('<HH', head[6:10])
+    elif imgtype == 'jpeg' and imgdata == None:
+        try:
+            fhandle.seek(0) # Read 0xff next
+            size = 2
+            ftype = 0
+            while not 0xc0 <= ftype <= 0xcf:
+                fhandle.seek(size, 1)
+                byte = fhandle.read(1)
+                while ord(byte) == 0xff:
+                    byte = fhandle.read(1)
+                ftype = ord(byte)
+                size = struct.unpack('>H', fhandle.read(2))[0] - 2
+            # We are at a SOFn block
+            fhandle.seek(1, 1)  # Skip `precision' byte.
+            height, width = struct.unpack('>HH', fhandle.read(4))
+        except Exception: #IGNORE:W0703
+            return
+    elif imgtype == 'jpeg' and imgdata != None:
+        try:
+            pos = 0
+            size = 2
+            ftype = 0
+            while not 0xc0 <= ftype <= 0xcf:
+                pos += size
+                byte = imgdata[pos]
+                pos += 1
+                while ord(byte) == 0xff:
+                    byte = imgdata[pos]
+                    pos += 1
+                ftype = ord(byte)
+                size = struct.unpack('>H', imgdata[pos:pos+2])[0] - 2
+                pos += 2
+            # We are at a SOFn block
+            pos += 1  # Skip `precision' byte.
+            height, width = struct.unpack('>HH', imgdata[pos:pos+4])
+            pos += 4
+        except Exception: #IGNORE:W0703
+            return
+    else:
+        return
+    return width, height
+
+# XXX experimental
+class CoverProcessor(object):
+    """Create a cover page.
+
+    """
+    def __init__(self, files, metadata, imgnames, imgname=None, imgdata=None):
+        self.files = files
+        self.metadata = metadata
+        self.imgnames = imgnames
+        self.cover_page = COVER_PAGE_FINENAME
+        self.use_svg = USE_SVG_WRAPPER # Use svg wrapper.
+        self.lang = metadata.get('Language', ['en'])[0]
+        if FORCE_DEFAULT_TITLE:
+            self.title = DEFAULT_TITLE
+        else:
+            self.title = metadata.get('Title', [DEFAULT_TITLE])[0]
+
+        self.cover_image = None
+        if imgname != None:
+            self.cover_image = imgname
+        elif 'CoverOffset' in metadata.keys():
+            imageNumber = int(metadata['CoverOffset'][0])
+            cover_image = self.imgnames[imageNumber]
+            if cover_image != None:
+                self.cover_image = cover_image
+            else:
+                print 'Warning: Cannot identify the cover image.'
+        if self.use_svg:
+            try:
+                if imgdata == None:
+                    fname = os.path.join(files.imgdir, self.cover_image)
+                    [self.width, self.height] = get_image_size(fname)
+                else:
+                    [self.width, self.height] = get_image_size(None, imgdata)
+            except:
+                self.use_svg = False
+            width = self.width
+            height = self.height
+            if width < 0 or height < 0 or width > MAX_WIDTH or height > MAX_HIEGHT:
+                self.use_svg = False
+        return
+
+    def getImageName(self):
+        return self.cover_image
+
+    def getXHTMLName(self):
+        return self.cover_page
+
+    def buildXHTML(self):
+        print 'Building a cover page.'
+        files = self.files
+        cover_image = self.cover_image
+        title = self.title
+        lang = self.lang
+
+        image_dir = os.path.normpath(os.path.relpath(files.k8images, files.k8text))
+        image_path = os.path.join(image_dir, cover_image).replace('\\', '/')
+
+        if not self.use_svg:
+            data = ''
+            data += '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html>'
+            data += '<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops"'
+            data += ' xml:lang="{:s}">\n'.format(lang)
+            data += '<head>\n<title>{:s}</title>\n'.format(title)
+            data += '<style type="text/css">\n'
+            data += 'body {\n  margin: 0;\n  padding: 0;\n  text-align: center;\n}\n'
+            data += 'div {\n  height: 100%;\n  width: 100%;\n  text-align: center;\n  page-break-inside: avoid;\n}\n'
+            data += 'img {\n  display: inline-block;\n  height: 100%;\n  margin: 0 auto;\n}\n'
+            data += '</style>\n</head>\n'
+            data += '<body><div>\n'
+            data += '  <img src="{:s}" alt=""/>\n'.format(image_path)
+            data += '</div></body>\n</html>'
+        else:
+            width = self.width
+            height = self.height
+            viewBox = "0 0 {0:d} {1:d}".format(width, height)
+
+            data = ''
+            data += '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html>'
+            data += '<html xmlns="http://www.w3.org/1999/xhtml"'
+            data += ' xml:lang="{:s}">\n'.format(lang)
+            data += '<head>\n  <title>{:s}</title>\n'.format(title)
+            data += '<style type="text/css">\n'
+            data += 'svg {padding: 0pt; margin:0pt}\n'
+            data += 'body { text-align: center; padding:0pt; margin: 0pt; }\n'
+            data += '</style>\n</head>\n'
+            data += '<body>\n  <div>\n'
+            data += '    <svg xmlns="http://www.w3.org/2000/svg" height="100%" preserveAspectRatio="xMidYMid meet" version="1.1" viewBox="{0:s}" width="100%" xmlns:xlink="http://www.w3.org/1999/xlink">\n'.format(viewBox)
+            data += '      <image height="{0}" width="{1}" xlink:href="{2}"></image>\n'.format(height, width, image_path)
+            data += '    </svg>\n'
+            data += '  </div>\n</body>\n</html>'
+        return data
+
+    def writeXHTML(self):
+        files = self.files
+        cover_page = self.cover_page
+
+        data = self.buildXHTML()
+
+        outfile = os.path.join(files.k8text, cover_page)
+        if os.path.exists(pathof(outfile)):
+            print 'Warning: {:s} already exists.'.format(cover_page)
+            #return
+            os.remove(pathof(outfile))
+        open(pathof(outfile), 'w').write(data)
+        return
+
+    def guide_toxml(self):
+        files = self.files
+        text_dir = os.path.relpath(files.k8text, files.k8oebps)
+        data = '<reference type="cover" title="Cover" href="{:s}/{:s}" />\n'.format(\
+                text_dir, self.cover_page)
+        return data
diff -u -N KindleUnpack_v072a/lib//mobi_k8resc.py KindleUnpack_v072b/lib//mobi_k8resc.py
--- KindleUnpack_v072a/lib//mobi_k8resc.py	2014-06-10 12:40:38 +0900
+++ KindleUnpack_v072b/lib//mobi_k8resc.py	2014-06-22 21:23:27 +0900
@@ -1,16 +1,15 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-# XXX Currently dom modules are not stable enough.
-#PROC_K8RESC_USE_DOM = False
-#""" Process K8 RESC section by dom modules. """
+DEBUG_TAGLIST = True
+import mobi_taglist as taglist
 
 import sys, os, re
-import xml.dom
-import xml.dom.minidom
-from path import pathof
+#import xml.dom
+#import xml.dom.minidom
+#from path import pathof
 
-class Metadata:
+class Metadata(object):
     """Class for metadata section.
 
     Data structure is the following:
@@ -24,8 +23,10 @@
     METADATA_COMMENT = 'comment'
     METADATA_END = 'end'
     def __init__(self):
-        self.data = None
-        #self.cover_id = None
+        self.data = []
+        self.refineids = set()
+        self.cover_id = None
+
         metadata_type = dict([ \
             [self.METADATA_START, 0], \
             [self.METADATA_DC, 1], \
@@ -47,18 +48,10 @@
         self.re_element = re.compile(r'''
                 (?P<comment><!--.*?-->)
             |
-                (?P<start_tag><(?P<tag>\S+).*?((?P<empty>/>)|>))
-                (?(empty)|(?P<content>[^<]*)(?P<end_tag></(?P=tag)>))
+                (?P<start_tag><(?P<tag>[^\s/>]+)(.*?>|.*?(?P<empty>/>)))
+                (?(empty)|(?P<content>.*?)(?P<end_tag></(?P=tag)>))
             ''', re.X|re.I|re.S)
 
-        re_pattern = ''
-        tag_types = self.tag_types
-        for tag_type in tag_types[:-1]:
-            re_pattern += '(?P<{}><{})|'.format(tag_type, tag_type)
-        else:
-            re_pattern += '(?P<{}><{})'.format(tag_types[-1], tag_types[-1])
-        self.re_tag_type = re.compile(re_pattern, re.I)
-
 
     def process(self, src):
         """Import metadata from src.
@@ -67,14 +60,13 @@
         re_metadata = self.re_metadata
         re_element = self.re_element
         re_attrib = self.re_attrib
-        re_tag_type = self.re_tag_type
 
         mo_meta = re_metadata.search(src)
         if mo_meta != None:
             data = []
             #[0:element, 1:type_id, 2:tag, 3:attribs, 4:isEmpty, 5:start_tag, 6:content, 7:end_tag]
             data.append([mo_meta.group(1), self.getTypeId(self.METADATA_START),
-                         None, None, None, None, None, None])
+                         None, {}, None, None, None, None])
 
             elements = mo_meta.group(2)
             pos = 0
@@ -83,27 +75,34 @@
                 if mo_element.group('comment') != None:
                     comment = mo_element.group()
                     data.append([mo_element.group(), self.getTypeId(self.METADATA_COMMENT),
-                                 None, None, None, None, None, None])
+                                 None, {}, None, None, None, None])
 
                 elif mo_element.group('start_tag') != None:
                     start_tag = mo_element.group('start_tag')
-                    mo_type = re_tag_type.match(start_tag)
-                    if mo_type == None:
-                        type_id = self.getTypeId(self.METADATA_OTHER)
+                    tag_prefix = mo_element.group('tag').split(':')[0]
+                    for tag in self.tag_types:
+                        if tag_prefix == tag:
+                            type_id = self.getTypeId(tag)
+                            break
                     else:
-                        for tag in self.tag_types:
-                            if mo_type.group(tag) != None:
-                                type_id = self.getTypeId(tag)
-                                break
-                            else:
-                                type_id = self.getTypeId(self.METADATA_OTHER)
-
+                        type_id = self.getTypeId(self.METADATA_OTHER)
                     tag_name = mo_element.group('tag')
                     content = mo_element.group('content')
                     end_tag = mo_element.group('end_tag')
                     attribs = dict(re_attrib.findall(start_tag))
                     isEmpty = mo_element.group('empty') != None
 
+                    id_ = attribs.get('refines', ' ')
+                    if id_[0] == '#':
+                        self.refineids.add(id_[1:])
+
+                    typeid_meta = self.getTypeId(self.METADATA_META)
+                    if type_id == typeid_meta:
+                        name = attribs.get('name')
+                        content = attribs.get('content')
+                        if name != None and name.lower() == 'cover':
+                            self.cover_id = content
+
                     #[0:element, 1:type_id, 2:tag, 3:attribs, 4:isEmpty, 5:start_tag, 6:content, 7:end_tag]
                     data.append([mo_element.group(), type_id,
                                  tag_name, attribs, isEmpty,
@@ -112,25 +111,36 @@
                 mo_element = re_element.search(elements, pos)
 
             data.append([mo_meta.group(3), self.getTypeId(self.METADATA_END),
-                         None, None, None, None, None, None])
+                         None, {}, None, None, None, None])
             self.data = data
-            #FIxME self.searchCoverId()
 
-    def metadata_toxml(self):
-        if self.data == None:
-            return []
+
+    def toxml(self, outall=False):
         metadata_ = []
-        num = self.getNumberOfElements()
-        for [element, typeid, tag, attribs, isEmpty, start, content, end] \
-                in self.getElements(range(1, num-1)):
-            #if typeid == self.getTypeId(resc_metadata.METADATA_COMMENT):
-            #    continue
-            if typeid >= 1:
-                if 'refines' in attribs:
-                    continue
+        if outall:
+            for [element, typeid, tag, attribs, isEmpty, start, content, end] in self.data[1:-1]:
                 metadata_.append(element + '\n')
+            return metadata_
+        for [element, typeid, tag, attribs, isEmpty, start, content, end] in self.data[1:-1]:
+            if 'refines' in attribs:
+                continue
+            metadata_.append(element + '\n')
         return metadata_
 
+    def getRefineMetadata(self, refineids):
+        metadata_ = []
+        for [element, typeid, tag, attribs, isEmpty, start, content, end] in self.data[1:-1]:
+            if attribs.get('refines') in refineids:
+                metadata_.append(element + '\n')
+        return metadata_
+
+    def getRefineIds(self):
+        return self.refineids
+
+    def getCoverId(self):
+        return self.cover_id
+
+
     def getTypeId(self, type_):
         return self.metadata_type.get(type_)
 
@@ -138,10 +148,7 @@
         return self.metadata_type_inv.get(type_id)
 
     def getNumberOfElements(self):
-        if self.data == None:
-            return 0
-        else:
-            return len(self.data)
+        return len(self.data)
 
     def getElement(self, index):
         """Return a sturctured metadata.
@@ -162,486 +169,350 @@
         else: #elif isinstance(indices, list):
             return [self.data[i] for i in indices]
 
-class K8RESCProcessor:
-    """RESC section processor, using re module.
 
-    """
-    def __init__(self, resc):
-        self.cover_id = None
-        self.xml_header = None
-        self.metadata = Metadata()
-        self.meta_types = None
-        self.spine = None #[itemref, skelid, itemid, isvalid, filename]
-        self.spine_skelid_dict = None
-        self.spine_filename_dict = None
 
-        if resc == None or len(resc) != 3:
-            return
-        [version, type_, data] = resc
-        self.version = version
-        self.type = type_
-        self.data = data
+class Spine(object):
+    """Class for spine section.
 
-        mo_xml = re.search(r'<\?xml[^>]*>', data, re.I)
-        if mo_xml != None:
-            self.xml_header = mo_xml.group()
-
-        self.metadata.process(data)
-        
-        # Find cover in metadata
-        metadata = self.metadata
-        typeid_meta = metadata.getTypeId(metadata.METADATA_META)
-        num = metadata.getNumberOfElements()
-        for [element, typeid, tag, attribs, isEmpty, start, content, end] \
-                in metadata.getElements(range(1, num-1)):
-            if typeid == typeid_meta:
-                name = attribs.get('name')
-                content = attribs.get('content')
-                if name != None and name.lower() == 'cover':
-                    self.cover_id = content
-                    break
+    Data structure is the following:
+    [0:tag, 1:itemid, 2:attribs, 3:skelid, 4:partno, 5:filename, 6:status]
+    """
+    IDX_TAG = 0
+    IDX_ITEMID = 1
+    IDX_ATTRIBS = 2
+    IDX_SKEL = 3
+    IDX_PARTNO = 4
+    IDX_FILENAME = 5
+    IDX_STATUS = 6
+    def __init__(self, k8proc):
+        self.k8proc = k8proc
+        self.data = []
+        self.skelid_to_index = {}
+        self.filename_to_index = {}
+        self.partno_to_index = {}
+
+    def process(self, src):
+        """Import spine from src.
 
-        mo_spine = re.search(r'(<spine[^>]*>)(.*?)(</spine>)', data, re.I|re.S)
+        """
+        mo_spine = re.search(r'(<spine[^>]*>)(.*?)(</spine>)', src, re.I|re.S)
         if mo_spine != None:
-            spine = []
-            spine.append([mo_spine.group(1), None, None, True, None])
+            data = []
+            data.append([mo_spine.group(1), '', '', None, -1, None, ''])
 
             # process itemrefs
             data_ = re.sub(r'<!--.*?-->', '', mo_spine.group(2), 0, re.S)
             itemrefs = re.findall(r'<[^>]*>', data_)
             re_idref = re.compile(r'(.*?)\s*idref="([^"]*)"(.*)', re.I)
             re_skelid = re.compile(r'(.*?)\s*skelid="([^"]*)"(.*)', re.I)
+            re_itemref = re.compile(r'<itemref(.*?)/>', re.I)
+
+            noskel_id = -1
             for itemref in itemrefs:
-                mo_idref = re_idref.search(itemref)
+                mo_itemref = re_itemref.search(itemref)
+                if mo_itemref == None:
+                    continue
+                tag = '<itemref/>'
+                attribs = mo_itemref.group(1)
+
+                mo_idref = re_idref.search(attribs)
                 if mo_idref != None:
-                    striped_itemref = mo_idref.group(1) + mo_idref.group(3)
+                    attribs = mo_idref.group(1) + mo_idref.group(3)
                     itemid = mo_idref.group(2)
                 else:
-                    striped_itemref = itemref
                     print 'Warning: no itemid in <itemref /> in the spine of RESC.'
                     break
-                
-                mo_skelid = re_skelid.search(striped_itemref)
+                mo_skelid = re_skelid.search(attribs)
                 if mo_skelid != None:
-                    striped_itemref = mo_skelid.group(1) + mo_skelid.group(3)
+                    attribs = mo_skelid.group(1) + mo_skelid.group(3)
                     if mo_skelid.group(2).isdigit():
                         skelid = int(mo_skelid.group(2))
                     else:
-                        skelid = -1
+                        skelid = noskel_id
+                        noskel_id -= 1
                 else:
-                    skelid = -1
-                
-                spine.append([striped_itemref, skelid, itemid, False, None])
+                    skelid = noskel_id
+                    noskel_id -= 1
+                data.append([tag, itemid, attribs, skelid, -1, None, ''])
             else:
-                spine.append(['</spine>', None, None, True, None])
-                #pairs = [[spine[i][1], i] for i in range(1, len(spine)-1)]
-                #spine_skelid_dict = dict(pairs)
-                self.spine = spine
-                self.createSkelidToSpineIndexDict()
+                data.append(['</spine>', '', '', None, -1, None, ''])
+                self.data = data
+                self.createSkelidDict()
+                self.createPartnoDict()
+
+        if not self.hasData():
+            # Make a spine if not able to retrieve from a RESC section.
+            n =  self.k8proc.getNumberOfParts()
+            self.create('skel', n)
+
+        # XXX:
+        # Get correspondences between itemes in a spine in RECS and ones in a skelton.
+        n =  self.k8proc.getNumberOfParts()
+        for i in range(n):
+            # Link to K8RescProcessor.
+            [skelnum, dir, filename, beg, end, aidtext] = self.k8proc.getPartInfo(i)
+            index = self.getIndexBySkelid(skelnum)
+            if index != None and index > 0 and index < len(self.data) - 1:
+                self.data[index][self.IDX_PARTNO] = i
+                #self.data[index][self.IDX_FILENAME] = filename
         return
 
 
-    def metadata_toxml(self):
-        return self.metadata.metadata_toxml()
+    def create(self, itemidbase, num):
+        """Create a dummy spine.
 
-    def spine_toxml(self):
-        spine = self.spine
+        """
+        data = [['<spine toc="ncx">', '', '', None, -1, None, '']]
+        for i in range(num):
+            data.append(['<itemref/>', 'skel{:d}'.format(i), '', i, -1, None, ''])
+        data.append(['</spine>', '', '', None, -1, None, ''])
+        self.data = data
+        self.createSkelidDict()
+        self.createPartnoDict()
+
+
+    def insert(self, i, itemid, attribs='', skelid=-1, filename=None):
+        """Insert a spine.
+
+        """
+        mo = re.search(r'^\s*?(\S.*)', attribs)
+        if mo != None:
+            attribs = ' ' + mo.group(1)
+        else:
+            attribs = ''
+        newdata = self.data[:i] \
+            + [['<itemref/>', itemid, attribs, skelid, -1, filename, '']] \
+            + self.data[i:]
+        self.data = newdata
+        self.createSkelidDict()
+        self.createPartnoDict()
+
+
+    def hasData(self):
+        return len(self.data) > 0
+
+    def getItemidList(self):
+        # Get itemids.
+        itemidlist = zip(*self.data)[self.IDX_ITEMID][1:-1]
+        return itemidlist
+
+    def toxml(self, dumpall=False):
+        # Return itemref taglist.
+        data = self.data
         spine_ = []
-        if spine != None:
-            re_itemref = re.compile(r'<itemref(.*?)/>', re.I)
-            for [itemref, skelid, itemid, isvalid, filename] in spine[1:-1]:
-                mo_itemref = re_itemref.search(itemref)
-                if isvalid and mo_itemref != None:
-                    elm = '<itemref idref="{:s}"{:s}/>'.format(itemid, mo_itemref.group(1))
-                    spine_.append(elm + '\n')
+        for [tag, itemid, attribs, skelid, partno, filename, status] in data[1:-1]:
+            if dumpall or status == 'used':
+                elm = '<itemref idref="{:s}"{:s}/>'.format(itemid, attribs)
+                spine_.append(elm + '\n')
         return spine_
 
-    def hasSpine(self):
-        return self.spine != None
+    def getEPUBVersion(self):
+        # Find epub version from itemref tags.
+        epubver = '2'
+        for attribs in zip(*self.data)[self.IDX_ATTRIBS][1:-1]:
+            if 'properties' in attribs.lower():
+                epubver = '3'
+                break
+        return epubver
 
-    def getSpineStartIndex(self):
+
+    def getStartIndex(self):
         return 1
 
-    def getSpineEndIndex(self):
-        return len(self.spine) - 1
+    def getEndIndex(self):
+        return len(self.data) - 1
 
-    def getSpineIndexBySkelid(self, skelid):
-        """Return corresponding itemref index to skelnum.
 
-        """
-        if self.spine_skelid_dict != None:
-            #[itemref, skelid, itemid, isvalid, filename]
-            index = self.spine_skelid_dict.get(skelid)
-        else:
-            index = None
-        return index
+    def getIndexBySkelid(self, skelid):
+        return self.skelid_to_index.get(skelid)
+
+    def getIndexByPartno(self, partno):
+        return self.partno_to_index.get(partno)
 
-    def getSpineIndexByFilename(self, filename):
-        spine_filename_dict = self.spine_filename_dict
+    def getIndexByFilename(self, filename):
+        filename_to_index = self.filename_to_index
         if filename == None:
             return None
-        elif spine_filename_dict == None:
-            return None
         else:
-            return spine_filename_dict.get(filename)
+            return filename_to_index.get(filename, None)
 
 
-    def getFilenameFromSpine(self, i):
-        if i != None:
-            #[itemref, skelid, itemid, isvalid, filename]
-            return self.spine[i][4]
-        else:
-            return None
+    #XXX:
+    def getFilename(self, i):
+        filename = self.data[i][self.IDX_FILENAME]
+        if filename != None:
+            return filename
+        partno = self.data[i][self.IDX_PARTNO]
+        if partno >= 0 and partno < self.k8proc.getNumberOfParts():
+            [skelnum, dir, filename, beg, end, aidtext] = self.k8proc.getPartInfo(partno)
+            return filename
+        return None
+
+    def getIdref(self, i):
+        return self.data[i][self.IDX_ITEMID]
+
+    def getAttribs(self, i):
+        return self.data[i][self.IDX_ATTRIBS]
+
+
+    def isLinkToPart(self, index):
+        partno = self.getPartno(index)
+        n = self.k8proc.getNumberOfParts()
+        return partno >= 0 and partno < n
+
 
-    def setFilenameToSpine(self, i, filename):
-        if i != None:
-            #[itemref, skelid, itemid, isvalid, filename]
-            self.spine[i][4] = filename
-
-    def getSpineSkelid(self, i):
-        #[itemref, skelid, itemid, isvalid, filename]
-        return self.spine[i][1]
-
-    def setSpineSkelid(self, i, skelid):
-        #[itemref, skelid, itemid, isvalid, filename]
-        self.spine[i][1] = skelid
-
-    def getSpineIdref(self, i):
-        #[itemref, skelid, itemid, isvalid, filename]
-        return self.spine[i][2]
-
-    def setSpineIdref(self, i, ref):
-        #[itemref, skelid, itemid, isvalid, filename]
-        self.spine[i][2] = ref
-        self.spine[i][3] = True
-        
-    def setSpineAttribute(self, i, name, content):
-        itemref = self.spine[i][0]
-        pa_attrib = r'''(?P<tag><itemref)(?:
-            ((?P<head>.*?)(?P<name>{:s})\s*=\s*"(?P<content>.*?)"(?P<tail>.*))
-            |(?P<nomatch>.*?/>))'''.format(name)
-        mo_attrib = re.search(pa_attrib, itemref, re.I + re.X)
+    def getPartno(self, i):
+        return self.data[i][self.IDX_PARTNO]
+
+    def getSkelid(self, i):
+        return self.data[i][self.IDX_SKEL]
+
+    #XXX:
+    def setFilename(self, i, filename):
+        self.data[i][self.IDX_FILENAME] = filename
+
+    def setIdref(self, i, ref):
+        self.data[i][self.IDX_ITEMID] = ref
+        self.data[i][self.IDX_STATUS] = True
+
+    def setSkelid(self, i, skelid):
+        self.data[i][self.IDX_SKEL] = skelid
+
+    def setStatus(self, i, status):
+        self.data[i][self.IDX_STATUS] = status
+
+    def setAttribute(self, i, name, content):
+        attribs = self.data[i][self.IDX_ATTRIBS]
+        pa_attrib = r'(?P<head>.*?)(?P<name>{:s})\s*=\s*"(?P<content>.*?)"(?P<tail>.*)'.format(name)
+        mo_attrib = re.search(pa_attrib, attribs)
         if mo_attrib != None:
-            if mo_attrib.group('content') != None:
-                new = mo_attrib.group('tag') + mo_attrib.group('head') \
-                    + '{:s}="{:s}"'.format(name, content) \
-                    + mo_attrib.group('tail')
-            else:
-                new = mo_attrib.group('tag') \
-                    + ' {:s}="{:s}"'.format(name, content) \
-                    + mo_attrib.group('nomatch')
-            self.spine[i][0] = new
-
-
-    def insertSpine(self, i, itemid, skelid=-1, filename=None):
-        newspine = self.spine[:i] \
-            + [['<itemref/>', skelid, itemid, False, filename]] \
-            + self.spine[i:]
-        self.spine = newspine
-
-    def createSkelidToSpineIndexDict(self):
-        spine = self.spine
-        if spine != None:
-            pairs = [[spine[i][1], i] for i in range(1, len(spine)-1)]
-            spine_skelid_dict = dict(pairs)
-            self.spine_skelid_dict = spine_skelid_dict
-
-    def createFilenameToSpineIndexDict(self):
-        spine = self.spine
-        if spine != None:
-            pairs = [[spine[i][4], i] for i in range(1, len(spine)-1)]
-            spine_filename_dict = dict(pairs)
-            self.spine_filename_dict = spine_filename_dict
-
-
-# XXX Currently dom modules are not stable enough.
-# insertSpine() function is not implemented.
-class K8RESCProcessorDom:
-    """RESC section processer using dom modules.
+            new = mo_attrib.group('head') \
+                + '{:s}="{:s}"'.format(name, content) \
+                + mo_attrib.group('tail')
+        else:
+            new =  attribs + ' {:s}="{:s}"'.format(name, content)
+        self.data[i][self.IDX_ATTRIBS] = new
+
+
+    def createSkelidDict(self):
+        indices = range(len(self.data))
+        skelids = zip(*self.data)[self.IDX_SKEL]
+        self.skelid_to_index = dict(zip(skelids, indices)[1:-1])
+
+    def createPartnoDict(self):
+        indices = range(len(self.data))
+        partnos = zip(*self.data)[self.IDX_PARTNO]
+        self.partno_to_index = dict(zip(partnos, indices)[1:-1])
+
+    def createFilenameDict(self):
+        indices = range(1, len(self.data) - 1)
+        #filenames = zip(*self.data)[self.IDX_FILENAME][1:-1]
+        filenames = [self.getFilename(i) for i in indices]
+        self.filename_to_index = dict(zip(filenames, indices))
+
+
+
+class K8RESCProcessor(object):
+    """RESC section processor, retrieve a spine and a metadata from RESC section.
 
     """
-    def __init__(self, resc):
-        self.cover_id = None
-        self.dom_metadata = None
-        self.dom_spine = None
-        self.metadata_array = None
-        self.spine_array = None
-        self.spine_skelid_dict = None
-        self.spine_filename_dict = None
+    def __init__(self, resc, k8proc):
+        self.xml_header = None
+        self.metadata = Metadata()
+        self.spine = Spine(k8proc)
+        self.k8proc = k8proc
 
         if resc == None or len(resc) != 3:
-            return
-        [version, type_, data] = resc
-        self.version = version
-        self.type = type_
-        self.data = data
-
-        # It seems to be able to handle utf-8 with a minidom module when
-        # modifying a xml string in the RESC section as below.
-        # However, it is not sure that the usage of minidom is proper.
-        resc_xml = ''
-        mo_xml = re.search(r'<\?xml[^>]*>', data, re.I)
-        if mo_xml != None:
-            resc_xml += mo_xml.group()
-        else:
-            resc_xml += '<?xml version="1.0" encoding="utf-8"?>'
-        mo_package = re.search(r'(<package[^>]*>).*?(</package>)', data, re.I)
-        if mo_package != None:
-            resc_xml += mo_package.group(1)
+            self.version = -1
+            self.type = -1
+            self.data = ''
         else:
-            resc_xml += '<package version="2.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="uid">'
-        #resc_xml += '<package version="2.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="uid">'
+            [version, type_, data] = resc
+            self.version = version
+            self.type = type_
+            self.data = data
 
-        mo_metadata = re.search(r'(<metadata[^>]*>).*?(</metadata>)', data, re.I)
-        if mo_metadata != None:
-            resc_xml += mo_metadata.group()
-        mo_spine = re.search(r'(<spine[^>]*>).*?(</spine>)', data, re.I)
-        if mo_spine != None:
-            resc_xml += mo_spine.group()
-        resc_xml += '</package>'
+            mo_xml = re.search(r'<\?xml[^>]*>', data, re.I)
+            if mo_xml != None:
+                self.xml_header = mo_xml.group()
 
-        dom = xml.dom.minidom.parseString(resc_xml)
-        dom_metadata = dom.getElementsByTagName('metadata')
-        if len(dom_metadata) > 0 and dom_metadata.item(0).hasChildNodes():
-            metadata_array = []
-            nodeList = dom_metadata.item(0).childNodes
-            for i in range(nodeList.length):
-                isvalid = True
-                item = nodeList.item(i)
-                if item.nodeType == xml.dom.Node.COMMENT_NODE:
-                    isvalid = False
-                elif item.hasAttributes():
-                    if item.hasAttribute('refines'):
-                        isvalid = False
-                    elif item.hasAttribute('name'):
-                        name = item.getAttribute('name')
-                        content = item.getAttribute('content').encode('utf-8')
-                        if name.lower() == 'cover':
-                            if len(content) > 0:
-                                self.cover_id = content
-                metadata_array.append([isvalid])
-
-            self.dom_metadata = dom_metadata
-            self.metadata_array = metadata_array
-
-        dom_spine = dom.getElementsByTagName('spine')
-        if len(dom_spine) > 0 and dom_spine.item(0).hasChildNodes():
-            nodeList = dom_spine.item(0).childNodes
-            spine_array = []
-            for i in range(nodeList.length):
-                item = nodeList.item(i)
-                if item.nodeType == xml.dom.Node.COMMENT_NODE:
-                    continue
-                elif item.hasAttributes():
-                    if item.hasAttribute('skelid'):
-                        skelid = int(item.getAttribute('skelid'))
-                        item.removeAttribute('skelid')
-                    else:
-                        skelid = -1
-                    spine_array.append([False, skelid, None])
+        self.metadata.process(self.data)
+        self.spine.process(self.data)
 
-            self.dom_spine = dom_spine
-            self.spine_array = spine_array
-            self.createSkelidToSpineIndexDict()
 
+    #def metadata_toxml(self, dumpall=False):
+    #    return self.metadata.toxml(dumpall)
 
-    def metadata_toxml(self):
-        metadata_ = []
-        dom_metadata = self.dom_metadata
-        metadata_array = self.metadata_array
-        if dom_metadata != None and len(dom_metadata) > 0 \
-        and dom_metadata.item(0).hasChildNodes():
-            nodeList = dom_metadata.item(0).childNodes
-            for i in range(nodeList.length):
-                if not metadata_array[i][0]:
-                    continue
-                item = nodeList.item(i)
-                metadata_.append(item.toxml('utf-8') + '\n')
-        return metadata_
+    #def getRefineMetadata(self, refineids):
+    #    return self.metadata.getRefineMetadata(refineids)
 
-    def spine_toxml(self):
-        spine_ = []
-        dom_spine = self.dom_spine
-        spine_array = self.spine_array
-        if dom_spine != None and len(dom_spine) > 0 \
-        and dom_spine.item(0).hasChildNodes():
-            nodeList = dom_spine.item(0).childNodes
-            for i in range(nodeList.length):
-                if not spine_array[i][0]:
-                    continue
-                item = nodeList.item(i)
-                spine_.append(item.toxml('utf-8') + '\n')
-        return spine_
+    #def getRefineIds(self):
+    #    return self.metadata.getRefineIds()
+
+    #def getCoverId(self):
+    #    return self.metadata.getCoverId()
+
+    #def spine_toxml(self, dumpall=False):
+    #    return self.spine.toxml(dumpall)
+
+    def getEPUBVersion(self):
+        return self.spine.getEPUBVersion()
 
     def hasSpine(self):
-        return self.dom_spine != None
+        return self.spine.hasData()
 
-    def getSpineStartIndex(self):
-        return 0
+    #def createSpine(self, itemidbase, num):
+    #    self.spine.create(itemidbase, num)
 
-    def getSpineEndIndex(self):
-        return len(self.spine_array)
+    #def insertCoverPageToSine(self, cover):
+    #    # Insert a cover page if not exist.
+    #    return self.spine.insertCoverPage(cover)
 
-    def getSpineIndexBySkelid(self, skelid):
-        """Return corresponding spine item index to skelid.
 
-        """
-        if self.spine_skelid_dict != None:
-            return self.spine_skelid_dict.get(skelid)
-        else:
-            return None
+    #def getSpineItemidList(self):
+    #    return self.spine.getItemidList()
 
-    def getSpineIndexByFilename(self, filename):
-        spine_filename_dict = self.spine_filename_dict
-        if filename == None:
-            return None
-        elif spine_filename_dict == None:
-            return None
-        else:
-            return spine_filename_dict.get(filename)
+    #def getSpineStartIndex(self):
+    #    return self.spine.getStartIndex()
 
-    def getFilenameFromSpine(self, i):
-        if i != None:
-            return self.spine_array[i][2]
-        else:
-            return None
+    #def getSpineEndIndex(self):
+    #    return self.spine.getEndIndex()
 
-    def setFilenameToSpine(self, i, filename):
-        if i != None:
-            self.spine_array[i][2] = filename
-
-    def getSpineIitem(self, i):
-        dom_spine = self.dom_spine
-        if dom_spine == None:
-            return None
-        if len(dom_spine) > 0 and dom_spine.item(0).hasChildNodes():
-            nodeList = dom_spine.item(0).childNodes
-            if i >= 0 and i < nodeList.length:
-                return nodeList.item(i)
-            else:
-                return None
+    #def getSpineIndexBySkelid(self, skelid):
+    #    return self.spine.getIndexBySkelid(skelid)
 
-    def getSpineSkelid(self, i):
-        skelid = None
-        item = self.getSpineIitem(i)
-        if item != None and item.nodeType != xml.dom.Node.COMMENT_NODE:
-            if item.hasAttribute('skelid'):
-                skelid = item.getAttribute('skelid').encode('utf-8')
-        return int(skelid)
-
-    def setSpineSkelid(self, i, skelid):
-        item = self.getSpineIitem(i)
-        if item != None and item.nodeType != xml.dom.Node.COMMENT_NODE:
-            if item.hasAttribute('skelid'):
-                item.removeAttribute('skelid')
-            item.setAttribute('skelid', str(skelid).decode('utf-8'))
-
-    def getSpineIdref(self, i):
-        idref = None
-        item = self.getSpineIitem(i)
-        if item != None and item.nodeType != xml.dom.Node.COMMENT_NODE:
-            if item.hasAttribute('idref'):
-                idref = item.getAttribute('idref').encode('utf-8')
-        return idref
-
-    def setSpineIdref(self, i, ref):
-        item = self.getSpineIitem(i)
-        if item != None and item.nodeType != xml.dom.Node.COMMENT_NODE:
-            if item.hasAttribute('idref'):
-                item.removeAttribute('idref')
-            item.setAttribute('idref', ref.decode('utf-8'))
-            self.spine_array[i][0] = True
-
-    def setSpineAttribute(self, i, name, content):
-        item = self.getSpineIitem(i)
-        if item != None and item.nodeType != xml.dom.Node.COMMENT_NODE:
-            if item.hasAttribute(name):
-                item.removeAttribute(name)
-            item.setAttribute(name, content.decode('utf-8'))
-
-
-    def createSkelidToSpineIndexDict(self):
-            spine_array = self.spine_array
-            pairs = [[spine_array[i][1], i] for i in range(len(spine_array))]
-            spine_skelid_dict = dict(pairs)
-            self.spine_skelid_dict = spine_skelid_dict
-
-    def createFilenameToSpineIndexDict(self):
-        spine_array = self.spine_array
-        if spine_array != None:
-            pairs = [[spine_array[i][2], i] for i in range(len(spine_array))]
-            spine_filename_dict = dict(pairs)
-            self.spine_filename_dict = spine_filename_dict
-
-
-# XXX experimental
-class CoverProcessor:
-    """Create a cover page.
+    #def getSpineIndexByFilename(self, filename):
+    #    return self.spine.getIndexByFilename(filename)
 
-    """
-    def __init__(self, files, metadata, imgnames):
-        self.files = files
-        self.metadata = metadata
-        self.imgnames = imgnames
-
-        self.cover_page = 'cover_page.xhtml'
-        self.cover_image = None
-        self.title = 'Untitled'
-        self.lang = 'en'
-        
-        if 'CoverOffset' in metadata.keys():
-            imageNumber = int(metadata['CoverOffset'][0])
-            cover_image = self.imgnames[imageNumber]
-            if cover_image != None:
-                self.cover_image = cover_image
-        title = metadata.get('Title')[0]
-        if title != None:
-            self.title = title
-        lang = metadata.get('Language')[0]
-        if lang != None:
-            self.lang = lang
-        return
-        
-    def getImageName(self):
-        return self.cover_image
-
-    def getXHTMLName(self):
-        return self.cover_page
-
-    def writeXHTML(self):
-        files = self.files
-        cover_page = self.cover_page
-        cover_image = self.cover_image
-        title = self.title
-        lang = self.lang
-
-        image_dir = os.path.relpath(files.k8images, files.k8text).replace('\\', '/')
-
-        data = ''
-        data += '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE html>'
-        data += '<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops"'
-        data += ' xml:lang="{:s}">\n'.format(lang)
-        data += '<head>\n<title>{:s}</title>\n'.format(title)
-        data += '<style type="text/css">\n'
-        data += 'body {\n\tmargin: 0;\n\tpadding: 0;\n\ttext-align: center;\n}\n'
-        data += 'div {\n\theight: 100%;\n\twidth: 100%;\n\ttext-align: center;\n\tpage-break-inside: avoid;\n}\n'
-        data += 'img {\n\tdisplay: inline-block;\n\theight: 100%;\n\tmargin: 0 auto;\n}\n'
-        data += '</style>\n</head>\n'
-        data += '<body><div>\n'
-        data += '\t<img src="{:s}/{:s}" alt=""/>\n'.format(image_dir, cover_image)
-        data += '</div></body>\n</html>'
-
-        outfile = os.path.join(files.k8text, self.cover_page)
-        if os.path.exists(pathof(outfile)):
-            print 'Warning: {:s} already exists.'.format(cover_page)
-            #return
-            os.remove(pathof(outfile))
-        open(pathof(outfile), 'w').write(data)
-        return
-        
-    def guide_toxml(self):
-        files = self.files
-        text_dir = os.path.relpath(files.k8text, files.k8oebps)
-        data = '<reference type="cover" title="Cover" href="{:s}/{:s}" />\n'.format(\
-                text_dir, self.cover_page)
-        return data
+    #def getFilenameFromSpine(self, i):
+    #    return self.spine.getFilename(i)
+
+    #def setFilenameToSpine(self, i, filename):
+    #    return self.spine.setFilename(i, filename)
+
+    #def getSpineSkelid(self, i):
+    #    return self.spine.getSkelid(i)
+
+    #def setSpineSkelid(self, i, skelid):
+    #    self.spine.setSkelid(i, skelid)
+
+    #def getSpineIdref(self, i):
+    #    return self.spine.getIdref(i)
+
+    #def setSpineIdref(self, i, ref):
+    #    self.spine.setIdref(i, ref)
+
+    #def setSpineStatus(self, i, valid):
+    #    self.spine.setStatus(i, valid)
+
+    #def setSpineAttribute(self, i, name, content):
+    #    self.spine.setAttribute(i, name, content)
+
+    #def insertSpine(self, i, itemid, skelid=-1, filename=None):
+    #    self.spine.insert(i, itemid, skelid, filename)
+
+    #def createSkelidToSpineIndexDict(self):
+    #    self.spine.createSkelidDict()
+
+    #def createFilenameToSpineIndexDict(self):
+    #    self.spine.createFilenameDict()
diff -u -N KindleUnpack_v072a/lib//mobi_nav.py KindleUnpack_v072b/lib//mobi_nav.py
--- KindleUnpack_v072a/lib//mobi_nav.py	1970-01-01 09:00:00 +0900
+++ KindleUnpack_v072b/lib//mobi_nav.py	2014-06-20 22:43:41 +0900
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+DEBUG_NAV = False
+
+FORCE_DEFAULT_TITLE = False
+""" Set to True to force to use the default title. """
+
+NAVIGATION_FINENAME = 'nav.xhtml'
+""" The name for the navigation document. """
+
+DEFAULT_TITLE = 'Navigation'
+""" The default title for the navigation document. """
+
+import sys, os, struct, re
+
+#from mobi_utils import toBase32
+#from mobi_index import MobiIndex
+from path import pathof
+
+class NAVProcessor(object):
+    def __init__(self, files, metadata):
+        self.files = files
+        self.metadata = metadata
+        self.navname = NAVIGATION_FINENAME
+
+    def buildLandmarks(self, guidetext):
+        header = ''
+        header += '  <nav epub:type="landmarks" id="landmarks" hidden="">\n'
+        header += '    <h2>Guide</h2>\n'
+        header += '    <ol>\n'
+        element = '      <li><a epub:type="{:s}" href="{:s}">{:s}</a></li>\n'
+        footer = ''
+        footer += '    </ol>\n'
+        footer += '  </nav>\n'
+
+        type_map = {
+            'cover' : 'cover',
+            'title-page' : 'title-page',
+            #?: 'frontmatter',
+            'text' : 'bodymatter',
+            #?: 'backmatter',
+            'toc' : 'toc',
+            'loi' : 'loi',
+            'lot' : 'lot',
+            'preface' : 'preface',
+            'bibliography' : 'bibliography',
+            'index' : 'index',
+            'glossary' : 'glossary',
+            'acknowledgements' : 'acknowledgements',
+            'colophon' : None,
+            'copyright-page' : None,
+            'dedication' : None,
+            'epigraph' : None,
+            'foreword' : None,
+            'notes' : None
+            }
+
+        re_type = re.compile(r'\s+type\s*=\s*"(.*?)"', re.I)
+        re_title = re.compile(r'\s+title\s*=\s*"(.*?)"', re.I)
+        re_link = re.compile(r'\s+href\s*=\s*"(.*?)"', re.I)
+        dir_ = os.path.relpath(self.files.k8text, self.files.k8oebps).replace('\\', '/')
+
+        data = ''
+        #guidetext = re.sub(r'<!--.*?-->', '', guidetext, 0, re.S)
+        references = re.findall(r'<reference\s+.*?>', guidetext, re.I)
+        for reference in references:
+            mo_type = re_type.search(reference)
+            mo_title = re_title.search(reference)
+            mo_link = re_link.search(reference)
+            if mo_type != None:
+                type_ = type_map.get(mo_type.group(1), None)
+            else:
+                type_ = None
+            if mo_title != None:
+                title = mo_title.group(1)
+            else:
+                title = None
+            if mo_link != None:
+                link = mo_link.group(1)
+            else:
+                link = None
+
+            if type_ != None and title != None and link != None:
+                link = os.path.relpath(link, dir_).replace('\\', '/')
+                data += element.format(type_, link, title)
+        if len(data) > 0:
+            return header + data + footer
+        else:
+            return ''
+
+    def buildTOC(self, indx_data):
+        header = ''
+        header += '  <nav epub:type="toc" id="toc">\n'
+        header += '    <h1>Table of contents</h1>\n'
+        footer = '  </nav>\n'
+
+        #recursive part
+        def recursINDX(max_lvl=0, num=0, lvl=0, start=-1, end=-1):
+            if start>len(indx_data) or end>len(indx_data):
+                print "Warning (in buildTOC): missing INDX child entries", start, end, len(indx_data)
+                return ''
+            if DEBUG_NAV:
+                print "recursINDX (in buildTOC) lvl %d from %d to %d" % (lvl, start, end)
+            xhtml = ''
+            if start <= 0:
+                start = 0
+            if end <= 0:
+                end = len(indx_data)
+            if lvl > max_lvl:
+                max_lvl = lvl
+
+            indent1 = '  ' * (2 + lvl * 2)
+            indent2 = '  ' * (3 + lvl * 2)
+            xhtml += indent1 + '<ol>\n'
+            for i in range(start, end):
+                e = indx_data[i]
+                htmlfile = e['filename']
+                desttag = e['idtag']
+                text = e['text']
+                if not e['hlvl'] == lvl:
+                    continue
+                num += 1
+                if desttag == '':
+                    link = htmlfile
+                else:
+                    link = '{:s}#{:s}'.format(htmlfile, desttag)
+                #open entry
+                #xhtml += indent2 + '<li id="toc{:d}">'.format(num)
+                xhtml += indent2 + '<li>'
+                entry = '<a href="{:}">{:s}</a>'.format(link, text)
+                #entry = '<span>{:s}</span>'.format(text)
+                xhtml += entry
+                #recurs
+                if e['child1'] >= 0:
+                    xhtml += '\n'
+                    xhtmlrec, max_lvl, num = recursINDX(max_lvl, num, lvl + 1,\
+                            e['child1'], e['childn'] + 1)
+                    xhtml += xhtmlrec
+                    xhtml += indent2
+                #close entry
+                xhtml += '</li>\n'
+            xhtml += indent1 + '</ol>\n'
+            return xhtml, max_lvl, num
+
+        data, max_lvl, num = recursINDX()
+        if not len(indx_data) == num:
+            print "Warning (in buildTOC): different number of entries in NCX", len(indx_data), num
+        return header + data + footer
+
+    def buildNAV(self, ncx_data, guidetext=None):
+        print "Building Navigation Document."
+        lang = self.metadata.get('Language', ['en'])[0]
+        if FORCE_DEFAULT_TITLE:
+            title = DEFAULT_TITLE
+        else:
+            title = self.metadata.get('Title', [DEFAULT_TITLE])[0]
+
+        nav_header = ''
+        nav_header +='<?xml version="1.0" encoding="utf-8"?>\n<!DOCTYPE html>'
+        nav_header += '<html xmlns="http://www.w3.org/1999/xhtml"'
+        nav_header += ' xmlns:epub="http://www.idpf.org/2011/epub"'
+        nav_header += ' lang="{0:s}" xml:lang="{0:s}">\n'.format(lang)
+        nav_header += '<head>\n<title>{:s}</title>\n'.format(title)
+        nav_header += '<style type="text/css">\n'
+        nav_header += 'nav#landmarks { display:none; }\n'
+        nav_header += '</style>\n</head>\n<body>\n'
+        nav_footer = '</body>\n</html>\n'
+
+        landmarks =  self.buildLandmarks(guidetext)
+        toc = self.buildTOC(ncx_data)
+
+        data = nav_header
+        data += landmarks
+        data += toc
+        data += nav_footer
+        return data
+
+    def getNAVName(self):
+        return self.navname
+
+    def writeNAV(self, ncx_data, guidetext=None):
+        # build the xhtml
+        #print "Write Navigation Document."
+        xhtml = self.buildNAV(ncx_data, guidetext)
+        fname = os.path.join(self.files.k8text, self.navname)
+        open(pathof(fname), 'wb').write(xhtml)
diff -u -N KindleUnpack_v072a/lib//mobi_opf.py KindleUnpack_v072b/lib//mobi_opf.py
--- KindleUnpack_v072a/lib//mobi_opf.py	2014-06-18 15:06:28 +0900
+++ KindleUnpack_v072b/lib//mobi_opf.py	2014-06-22 22:42:17 +0900
@@ -1,13 +1,48 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
+EPUB3_WITH_NCX = True # Do not set to False except for debug.
+""" Set to True to create a toc.ncx when converting to epub3. """
+
+EPUB3_WITH_GUIDE = True # Do not set to False except for debug.
+""" Set to True to create a guide element in an opf when converting to epub3. """
+
+DEBUG = False
+if DEBUG:
+    EPUB3_WITH_NCX = False
+    EPUB3_WITH_GUIDE = False
+
+
+OPF_NAME = 'content.opf'
+""" The name for the OPF of EPUB. """
+
+TOC_NCX = 'toc.ncx'
+""" The name for the TOC of EPUB2. """
+
+NAVIGATION_DOCUMENT = 'nav.xhtml'
+""" The name for the navigation document of EPUB3. """
+
+BEGIN_INFO_ONLY = '<!-- BEGIN INFORMATION ONLY -->'
+""" The comment to indicate the beginning of metadata which will be ignored by kindlegen. """
+
+END_INFO_ONLY = '<!-- END INFORMATION ONLY -->'
+""" The comment to indicate the end of metadata which will be ignored by kindlegen. """
+
+PPD_KEY = 'page-progression-direction'
+""" The meta name for page-progression-direction. """
+
+PWM_KEY = 'primary-writing-mode'
+""" The meta name for primary-writing-mode. """
+
+
 import sys, os, re, uuid
 from path import pathof
 from xml.sax.saxutils import escape as xmlescape
 from HTMLParser import HTMLParser
 EXTRA_ENTITIES = {'"':'&quot;', "'":"&apos;"}
+import mobi_taglist as taglist
 
-class OPFProcessor:
+class OPFProcessor(object):
     def __init__(self, files, metadata, filenames, imgnames, isNCX, mh, usedmap, pagemapxml='', guidetext=False, k8resc=None):
         self.files = files
         self.metadata = metadata
@@ -21,12 +56,24 @@
         self.used = usedmap
         self.k8resc = k8resc
         self.covername = None
+        self.cover_id = None
+        self.resc_metadata = []
+        self.extra_metadata_head = []
+        self.extra_metadata_tail = []
         self.h = HTMLParser()
         # Create a unique urn uuid
         self.BookId = str(uuid.uuid4())
         self.starting_offset = None
         self.page_progression_direction = None
         self.pagemap = pagemapxml
+        self.ncxname = None
+        self.navname = None
+        self.title_ids = []
+        self.creator_ids = []
+        self.publisher_ids = []
+        # Parse RESC.
+        self.parseK8RESC()
+
 
     def escapeit(self, sval, EXTRAS=None):
         # note, xmlescape and unescape do not work with utf-8 bytestrings
@@ -38,12 +85,67 @@
             ures = xmlescape(self.h.unescape(uval))
         return ures.encode('utf-8')
 
-    def writeOPF(self, has_obfuscated_fonts=False):
-        # write out the metadata as an OEB 1.0 OPF file
-        print "Write opf"
-        metadata = self.metadata
+    def createItemidSet(self):
+        # Create an id set to prevent id confliction.
+        k8resc = self.k8resc
+        cover_id = self.cover_id
+        if k8resc != None and k8resc.hasSpine():
+            itemidset = set(k8resc.spine.getItemidList())
+        else:
+            itemidset = set()
+        if cover_id != None:
+            itemidset.add(cover_id)
+        return itemidset
+
+    def createItemid(self, base, itemidset):
+        # Create an itemid.
+            idsuf = 0
+            itemid = base
+            while itemid in itemidset:
+                itemid = '{0:s}-{1:d}'.format(base, idsuf)
+                idsuf += 1
+            itemidset.add(itemid)
+            return itemid
+
+
+    # Call this function prior to call buildOPFMetadata().
+    def parseK8RESC(self):
+        # Parse K8 RESC metadata.
         k8resc = self.k8resc
+        if k8resc != None:
+            resc_metadata_ = k8resc.metadata.toxml()
+            cover_id = k8resc.metadata.getCoverId()
+            if cover_id != None:
+                self.cover_id = cover_id
+
+            # XXX: Violated epub definitions?
+            #indices = taglist.findall(resc_metadata_, 'dc-metadata')
+            #for i in indices:
+            #    mo = taglist.re_element.search(resc_metadata_[i])
+            #    if mo != None:
+            #        start_tag = mo.group('start_tag')
+            #        end_tag = mo.group('end_tag')
+            #        self.extra_metadata_head.append(start_tag +'\n')
+            #        self.extra_metadata_tail = [end_tag +'\n'] + self.extra_metadata_tail
+            #        resc_metadata_[i] = ''
+
+            indices = taglist.findall(resc_metadata_, '<!--')
+            indices += taglist.findall(resc_metadata_, 'dc-metadata')
+            #indices += taglist.findall(resc_metadata_, 'meta', 'refines')
+            # Search metadata generated by kindleunpack previously.
+            indices_start = taglist.findall(resc_metadata_, BEGIN_INFO_ONLY)
+            indices_end = taglist.findall(resc_metadata_, END_INFO_ONLY)
+            if len(indices_start) > 0 and len(indices_end) > 0 and len(indices_start) == len(indices_end):
+                for istart, iend in zip(indices_start, indices_end):
+                    indices += range(istart, iend + 1)
+            new_metadata = taglist.remove_tags(resc_metadata_, indices)
+            self.resc_metadata = new_metadata
+
 
+    def buildOPFMetadata(self, start_tag, has_obfuscated_fonts=False):
+        # Build metadata.
+        metadata = self.metadata
+        #k8resc = self.k8resc
         META_TAGS = ['Drm Server Id', 'Drm Commerce Id', 'Drm Ebookbase Book Id', 'ASIN', 'ThumbOffset', 'Fake Cover',
                                                 'Creator Software', 'Creator Major Version', 'Creator Minor Version', 'Creator Build Number',
                                                 'Watermark', 'Clipping Limit', 'Publisher Limit', 'Text to Speech Disabled', 'CDE Type',
@@ -72,9 +174,9 @@
                 del metadata[key]
 
         data = []
-        data.append('<?xml version="1.0" encoding="utf-8"?>\n')
-        data.append('<package version="2.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="uid">\n')
-        data.append('<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">\n')
+        #data.append('<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">\n')
+        data.append(start_tag + '\n')
+        data += self.extra_metadata_head
         # Handle standard metadata
         if 'Title' in metadata.keys():
             handleTag(data, metadata, 'Title', 'dc:title')
@@ -119,24 +221,18 @@
         handleTag(data, metadata, 'DictOutLanguage', 'DictionaryOutLanguage')
 
        # page-progression-direction
-        ppd_key = 'page-progression-direction'
-        self.page_progression_direction = metadata.pop(ppd_key, [None])[0]
-        pwm_key = 'primary-writing-mode'
-        pwm_value = metadata.pop(pwm_key, [None])[0]
+        self.page_progression_direction = metadata.pop(PPD_KEY, [None])[0]
+        pwm_value = metadata.pop(PWM_KEY, [None])[0]
         if pwm_value != None:
-            data.append('<meta name="'+pwm_key+'" content="'+self.escapeit(pwm_value, EXTRA_ENTITIES)+'" />\n')
+            data.append('<meta name="'+PWM_KEY+'" content="'+self.escapeit(pwm_value, EXTRA_ENTITIES)+'" />\n')
             if 'rl' in pwm_value:
                 self.page_progression_direction = 'rtl'
 
         # Append metadata in RESC section.
-        cover_id = None
-        if k8resc != None:
-            cover_id = k8resc.cover_id
-            resc_metadata_ = k8resc.metadata_toxml()
-            if len(resc_metadata_) > 0:
-                data.append('<!-- Begin imported from RESC section -->\n')
-                data += resc_metadata_
-                data.append('<!-- End imported from RESC section -->\n')
+        if len(self.resc_metadata) > 0:
+            data.append('<!-- Begin imported from RESC section -->\n')
+            data += self.resc_metadata
+            data.append('<!-- End imported from RESC section -->\n')
 
         if 'CoverOffset' in metadata.keys():
             imageNumber = int(metadata['CoverOffset'][0])
@@ -144,11 +240,14 @@
             if self.covername is None:
                 print "Error: Cover image %s was not recognized as a valid image" % imageNumber
             else:
-                if cover_id == None:
-                    cover_id = 'cover_img'
-                    data.append('<meta name="cover" content="' + cover_id + '" />\n')
+                if self.cover_id == None:
+                    self.cover_id = 'cover_img'
+                    data.append('<meta name="cover" content="' + self.cover_id + '" />\n')
                 self.used[self.covername] = 'used'
             del metadata['CoverOffset']
+        i = taglist.find(data, 'meta', 'name', 'output encoding')
+        if i != None:
+            data[i] = ''
         handleMetaPairs(data, metadata, 'Codec', 'output encoding')
         # handle kindlegen specifc tags
         handleMetaPairs(data, metadata, 'RegionMagnification', 'RegionMagnification')
@@ -173,6 +272,7 @@
                     data.append('<SRP Currency="'+currencyList[i]+'">'+priceList[i]+'</SRP>\n')
             del metadata['Price']
             del metadata['Currency']
+        data.append(BEGIN_INFO_ONLY + '\n')
         data.append("<!-- The following meta tags are just for information and will be ignored by mobigen/kindlegen. -->\n")
         if 'ThumbOffset' in metadata.keys():
             imageNumber = int(metadata['ThumbOffset'][0])
@@ -197,8 +297,95 @@
                     self.starting_offset = value
                 data.append('<meta name="'+key+'" content="'+self.escapeit(value, EXTRA_ENTITIES)+'" />\n')
             del metadata[key]
+        data.append(END_INFO_ONLY + '\n')
+        data += self.extra_metadata_tail
         data.append('</metadata>\n')
+        return data
+
+    # XXX: In this version, building manifest functions are splited into
+    # for mobi7/azw4, for epub2 and for epub3 in order to undurstand easier
+    # and to make safer for further extentions.
+    # However; buildEPUB3OPFManifest() should work for all versions.
+    # So it is possible to use one common function.
+    # Note, this is not final.
+    def buildOPFManifest(self, ncxname):
+        # Build manifest for mobi7 and azw4.
+        cover_id = self.cover_id
+        self.ncxname = ncxname
+
+        data = []
+        data.append('<manifest>\n')
+        media_map = {
+                '.jpg'  : 'image/jpeg',
+                '.jpeg' : 'image/jpeg',
+                '.png'  : 'image/png',
+                '.gif'  : 'image/gif',
+                '.svg'  : 'image/svg+xml',
+                '.xhtml': 'application/xhtml+xml',
+                '.html' : 'text/x-oeb1-document',
+                '.pdf'  : 'application/pdf'
+                #'.ttf'  : 'application/x-font-ttf',
+                #'.otf'  : 'application/x-font-opentype',
+                #'.css'  : 'text/css'
+                }
+        spinerefs = []
+        # Create an id set to prevent id confliction.
+        itemidset = self.createItemidSet()
+        idcnt = 0
+        for [dir,fname] in self.filenames:
+            name, ext = os.path.splitext(fname)
+            ext = ext.lower()
+            media = media_map.get(ext)
+            #ref = "item%d" % idcnt
+            ref = self.createItemid('item{:d}'.format(idcnt), itemidset)
+            if dir != '':
+                data.append('<item id="' + ref + '" media-type="' + media + '" href="' + dir + '/' + fname +'" />\n')
+            else:
+                data.append('<item id="' + ref + '" media-type="' + media + '" href="' + fname +'" />\n')
+            if ext in ['.xhtml', '.html']:
+                spinerefs.append(ref)
+            idcnt += 1
+
+        for fname in self.imgnames:
+            if fname != None:
+                if self.used.get(fname,'not used') == 'not used':
+                    continue
+                name, ext = os.path.splitext(fname)
+                ext = ext.lower()
+                media = media_map.get(ext,ext[1:])
+                if fname == self.covername:
+                    ref = cover_id
+                else:
+                    #ref = "item%d" % idcnt
+                    ref = self.createItemid('item{:d}'.format(idcnt), itemidset)
+                # fonts only exist in K8 ebooks
+                #if ext == '.ttf' or ext == '.otf':
+                #    data.append('<item id="' + ref + '" media-type="' + media + '" href="Fonts/' + fname +'" />\n')
+                #else:
+                #    data.append('<item id="' + ref + '" media-type="' + media + '" href="Images/' + fname +'" />\n')
+                data.append('<item id="' + ref + '" media-type="' + media + '" href="Images/' + fname +'" />\n')
+                idcnt += 1
+
+        if ncxname != None:
+            data.append('<item id="ncx" media-type="application/x-dtbncx+xml" href="' + ncxname +'"></item>\n')
+        if self.pagemap != '':
+            data.append('<item id="map" media-type="application/oebs-page-map+xml" href="page-map.xml"></item>\n')
+        data.append('</manifest>\n')
+        return [data, spinerefs]
 
+
+    # This function is almost same to building manifest in writeOPF() of v0.72a,
+    # manifest in output opf will be identical to v0.72a for mobi7, azw4 and mobi8.
+    def buildEPUB2OPFManifest(self, ncxname):
+        # for EPUB2 manifest.
+        k8resc = self.k8resc
+        cover_id = self.cover_id
+        hasK8RescSpine = k8resc != None and k8resc.hasSpine()
+        if hasK8RescSpine:
+            k8resc.spine.createFilenameDict()
+        self.ncxname = ncxname
+
+        data = []
         # build manifest
         data.append('<manifest>\n')
         media_map = {
@@ -217,38 +404,19 @@
         spinerefs = []
 
         # Create an id set to prevent id confliction.
-        if k8resc != None and k8resc.hasSpine():
-            istart = k8resc.getSpineStartIndex()
-            iend = k8resc.getSpineEndIndex()
-            itemidset = set(zip(*k8resc.spine)[2][istart:iend])
-        else:
-            itemidset = set()
-        if cover_id != None:
-            itemidset.add(cover_id)
-
-        def create_itemid(idcnt):
-            idsuf = 0
-            itemid = "item{0:d}".format(idcnt)
-            while itemid in itemidset:
-                itemid = "item{0:d}-{1:d}".format(idcnt, idsuf)
-                idsuf += 1
-            itemidset.add(itemid)
-            return itemid
+        itemidset = self.createItemidSet()
 
         idcnt = 0
         for [dir,fname] in self.filenames:
             name, ext = os.path.splitext(fname)
             ext = ext.lower()
             media = media_map.get(ext)
-            ref = create_itemid(idcnt)
-            if k8resc != None and k8resc.hasSpine():
-                index = k8resc.getSpineIndexByFilename(fname)
+            ref = self.createItemid('item{:d}'.format(idcnt), itemidset)
+            if hasK8RescSpine:
+                index = k8resc.spine.getIndexByFilename(fname)
                 if index != None:
-                    itemid = k8resc.getSpineIdref(index)
-                    #ref += '_' + itemid
-                    ref = itemid
-                    k8resc.setSpineIdref(index, ref)
-
+                    ref = k8resc.spine.getIdref(index)
+                    k8resc.spine.setStatus(index, 'used')
             if dir != '':
                 data.append('<item id="' + ref + '" media-type="' + media + '" href="' + dir + '/' + fname +'" />\n')
             else:
@@ -266,57 +434,424 @@
                 media = media_map.get(ext,ext[1:])
                 if fname == self.covername:
                     ref = cover_id
-                    # ref += '" properties="cover-image'
                 else:
-                    ref = create_itemid(idcnt)
-
+                    ref = self.createItemid('item{:d}'.format(idcnt), itemidset)
                 if ext == '.ttf' or ext == '.otf':
                     data.append('<item id="' + ref + '" media-type="' + media + '" href="Fonts/' + fname +'" />\n')
                 else:
                     data.append('<item id="' + ref + '" media-type="' + media + '" href="Images/' + fname +'" />\n')
                 idcnt += 1
+        data.append('<item id="ncx" media-type="application/x-dtbncx+xml" href="' + ncxname +'"></item>\n')
+        if self.pagemap != '':
+            data.append('<item id="map" media-type="application/oebs-page-map+xml" href="page-map.xml"></item>\n')
+        data.append('</manifest>\n')
+        return [data, spinerefs]
 
-        if self.isNCX:
-            if self.isK8:
-                ncxname = 'toc.ncx'
+
+    # This function should work not only for epub3 but also for mobi7, azw4 and epub2.
+    def buildEPUB3OPFManifest(self, ncxname, navname=None):
+        # Build manifest for epub3.
+        files = self.files
+        k8resc = self.k8resc
+        cover_id = self.cover_id
+        hasK8RescSpine = k8resc != None and k8resc.hasSpine()
+        if hasK8RescSpine:
+            k8resc.spine.createFilenameDict()
+        isEpub3 = False
+        if navname != None:
+            isEpub3 = True
+        self.ncxname = ncxname
+        self.navname = navname
+
+        data = []
+        # build manifest
+        data.append('<manifest>\n')
+        media_map = {
+                '.jpg'  : 'image/jpeg',
+                '.jpeg' : 'image/jpeg',
+                '.png'  : 'image/png',
+                '.gif'  : 'image/gif',
+                '.svg'  : 'image/svg+xml',
+                '.xhtml': 'application/xhtml+xml',
+                '.html' : 'text/x-oeb1-document', # obsoleted?
+                '.pdf'  : 'application/pdf', # for azw4(print replica textbook)
+                '.ttf'  : 'application/x-font-ttf',
+                '.otf'  : 'application/x-font-opentype', # replaced?
+                #'.otf' : 'application/vnd.ms-opentype', # [OpenType] OpenType fonts
+                #'.woff' : 'application/font-woff', # [WOFF] WOFF fonts
+                #'.smil' : 'application/smil+xml', # [MediaOverlays301] EPUB Media Overlay documents
+                #'.pls' : 'application/pls+xml', # [PLS] Text-to-Speech (TTS) Pronunciation lexicons
+                #'.mp3'  : 'audio/mpeg',
+                #'.mp4'  : 'audio/mp4',
+                #'.js'   : 'text/javascript', # not supported in K8
+                '.css'  : 'text/css'
+                }
+        spinerefs = []
+
+        # Create an id set to prevent id confliction.
+        itemidset = self.createItemidSet()
+
+        idcnt = 0
+        for [dir,fname] in self.filenames:
+            name, ext = os.path.splitext(fname)
+            ext = ext.lower()
+            media = media_map.get(ext)
+            ref = self.createItemid('item{:d}'.format(idcnt), itemidset)
+            properties = ''
+            if hasK8RescSpine:
+                index = k8resc.spine.getIndexByFilename(fname)
+                if index != None:
+                    ref = k8resc.spine.getIdref(index)
+                    k8resc.spine.setStatus(index, 'used')
+            if dir != '':
+                fpath = dir + '/' + fname
             else:
-                ncxname = self.files.getInputFileBasename() + '.ncx'
+                fpath = fname
+            data.append('<item id="{0:}" media-type="{1:}" href="{2:}" {3:}/>\n'.format(ref, media, fpath, properties))
+
+            if ext in ['.xhtml', '.html']:
+                spinerefs.append(ref)
+            idcnt += 1
+
+        for fname in self.imgnames:
+            if fname != None:
+                if self.used.get(fname,'not used') == 'not used':
+                    continue
+                name, ext = os.path.splitext(fname)
+                ext = ext.lower()
+                media = media_map.get(ext,ext[1:])
+                properties = ''
+                if fname == self.covername:
+                    ref = cover_id
+                    if isEpub3:
+                        properties = 'properties="cover-image"'
+                else:
+                    ref = self.createItemid('item{:d}'.format(idcnt), itemidset)
+                if ext == '.ttf' or ext == '.otf':
+                    fpath = 'Fonts/' + fname
+                else:
+                    fpath = 'Images/' + fname
+                data.append('<item id="{0:}" media-type="{1:}" href="{2:}" {3:}/>\n'.format(ref, media, fpath, properties))
+                idcnt += 1
+
+        if navname != None:
+            media = 'application/xhtml+xml'
+            ref = self.createItemid('nav', itemidset)
+            properties = 'properties="nav"'
+            #textdir = os.path.relpath(files.k8text, files.k8oebps).replace('\\', '/') + '/'
+            #fpath = textdir + navname
+            fpath = 'Text/' + navname
+            data.append('<item id="{0:}" media-type="{1:}" href="{2:}" {3:}/>\n'.format(ref, media, fpath, properties))
+        if ncxname != None:
             data.append('<item id="ncx" media-type="application/x-dtbncx+xml" href="' + ncxname +'"></item>\n')
         if self.pagemap != '':
             data.append('<item id="map" media-type="application/oebs-page-map+xml" href="page-map.xml"></item>\n')
         data.append('</manifest>\n')
+        return [data, spinerefs]
 
+
+    def buildOPFSpine(self, spinerefs, isNCX):
         # build spine
-        spine_start_tag = '<spine'
-        if self.page_progression_direction != None:
-            spine_start_tag += ' page-progression-direction="{:s}"'.format(self.page_progression_direction)
-        if self.isNCX:
-            spine_start_tag += ' toc="ncx"'
-        spine_start_tag += '>\n'
+        k8resc = self.k8resc
+        hasK8RescSpine = k8resc != None and k8resc.hasSpine()
+        data = []
+        ppd = ''
+        if self.isK8 and self.page_progression_direction != None:
+            ppd = ' page-progression-direction="{:s}"'.format(self.page_progression_direction)
+        ncx = ''
+        if isNCX:
+            ncx = ' toc="ncx"'
+        spine_start_tag = '<spine{:s}{:s}>\n'.format(ppd, ncx)
         data.append(spine_start_tag)
-        if k8resc != None and k8resc.hasSpine():
-            spine_ = k8resc.spine_toxml()
+        if hasK8RescSpine:
+            spine_ = k8resc.spine.toxml()
+        else:
+            spine_ = []
+        if len(spine_) > 0:
             data += spine_
         else:
             for entry in spinerefs:
                 data.append('<itemref idref="' + entry + '"/>\n')
         data.append('</spine>\n')
+        return data
 
-        data.append('<tours>\n</tours>\n')
 
+    def buildOPF(self):
+        # Build an OPF for mobi7 and azw4.
+        print "Building an opf for mobi7/azw4."
+        data = []
+        data.append('<?xml version="1.0" encoding="utf-8"?>\n')
+        data.append('<package version="2.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="uid">\n')
+        metadata_tag = '<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">'
+        opf_metadata = self.buildOPFMetadata(metadata_tag)
+        data += opf_metadata
+        if self.isNCX:
+            ncxname = self.files.getInputFileBasename() + '.ncx'
+        else:
+            ncxname = None
+        [opf_manifest, spinerefs] = self.buildOPFManifest(ncxname)
+        data += opf_manifest
+        opf_spine = self.buildOPFSpine(spinerefs, self.isNCX)
+        data += opf_spine
+        data.append('<tours>\n</tours>\n')
+        #opf_guide = self.buildOPFGuide()
+        #data += opf_guide
         if not self.printReplica:
             metaguidetext = ''
-            if not self.isK8:
-                # get guide items from metadata (note starting offset previsouly processed)
-                if self.starting_offset != None:
-                    so = self.starting_offset
-                    metaguidetext += '<reference type="text" href="'+self.filenames[0][1]+'#filepos'+so+'" />\n'
-            data.append('<guide>\n' + metaguidetext + self.guidetext + '</guide>\n')
+            # get guide items from metadata (note starting offset previsouly processed)
+            if self.starting_offset != None:
+                so = self.starting_offset
+                metaguidetext += '<reference type="text" href="'+self.filenames[0][1]+'#filepos'+so+'" />\n'
+            guide ='<guide>\n' + metaguidetext + self.guidetext + '</guide>\n'
+            data.append(guide)
+        data.append('</package>\n')
+        return ''.join(data)
+
+
+    def buildEPUB2OPF(self, has_obfuscated_fonts=False):
+        # Build an OPF for EPUB2.
+        print "Building an opf for epub2"
+        self.epubver = '2'
+        data = []
+        data.append('<?xml version="1.0" encoding="utf-8"?>\n')
+        data.append('<package version="2.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="uid">\n')
+        metadata_tag = '<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">'
+        opf_metadata = self.buildOPFMetadata(metadata_tag, has_obfuscated_fonts)
+        data += opf_metadata
+        [opf_manifest, spinerefs] = self.buildEPUB2OPFManifest(TOC_NCX)
+        data += opf_manifest
+        opf_spine = self.buildOPFSpine(spinerefs, True)
+        data += opf_spine
+        data.append('<tours>\n</tours>\n')
+        #opf_guide = self.buildOPFGuide()
+        #data += opf_guide
+        guide ='<guide>\n' + self.guidetext + '</guide>\n'
+        data.append(guide)
+        data.append('</package>\n')
+        return ''.join(data)
+
+
+    def buildEPUB3OPF(self, has_obfuscated_fonts=False):
+        # Build an OPF for EPUB3.
+        print "Building an opf for epub3"
+        self.epubver = '3'
+        has_ncx = EPUB3_WITH_NCX
+        has_guide = EPUB3_WITH_GUIDE
+        self.sovleRefineID()
+        data = []
+        data.append('<?xml version="1.0" encoding="utf-8"?>\n')
+        data.append('<package version="3.0" xmlns="http://www.idpf.org/2007/opf" prefix="rendition: http://www.idpf.org/vocab/rendition/#" unique-identifier="uid">\n')
+        #metadata_tag = '<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">') # XXX: kobo cannot process.
+        metadata_tag = '<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">'
+        opf_metadata = self.buildOPFMetadata(metadata_tag, has_obfuscated_fonts)
+        newmetadata = self.convertAmazonMetadataToEPUB3(opf_metadata)
+        opf_metadata = newmetadata
+        ## Delete obsoleted meta
+        #i = taglist.find(opf_metadata, 'meta', 'name', 'cover')
+        #opf_metadata = opf_metadata[:i] + opf_metadata[i+1:]
+        data += opf_metadata
+        [opf_manifest, spinerefs] = self.buildEPUB3OPFManifest(TOC_NCX, NAVIGATION_DOCUMENT)
+        data += opf_manifest
+
+        opf_spine = self.buildOPFSpine(spinerefs, has_ncx)
+        data += opf_spine
+        #data.append('<tours>\n</tours>\n')
+        if has_guide:
+            guide ='<guide>\n' + self.guidetext + '</guide>\n'
+            data.append(guide)
         data.append('</package>\n')
+        return ''.join(data)
+
+
+    def buildK8OPF(self, has_obfuscated_fonts=False, epubver='2'):
+        # Build an OPF for mobi8.
+        # The target epub version is switched by epubver,
+        #   'a' or 'auto' : determine automatically.
+        #   '2' or 'epub2': focre to epub2.
+        #   '3' or 'epub3': focre to epub3.
+        if epubver.lower() in ['a', 'auto']:
+            epubver = self.sovleEPUBVersion()
+
+        if epubver.lower() in ['2', 'epub2']:
+            return self.buildEPUB2OPF(has_obfuscated_fonts)
+        elif epubver.lower() in ['3', 'epub3']:
+            return self.buildEPUB3OPF(has_obfuscated_fonts)
+        else:
+            return self.buildEPUB2OPF(has_obfuscated_fonts)
+
+
+    def writeOPF(self, has_obfuscated_fonts=False, epubver='2'):
+        # write out the metadata as an OEB 1.0 OPF file
+        #print "Write opf"
         if self.isK8:
-            outopf = os.path.join(self.files.k8oebps,'content.opf')
+            return self.writeK8OPF(has_obfuscated_fonts, epubver)
         else:
+            data = self.buildOPF()
             outopf = os.path.join(self.files.mobi7dir, self.files.getInputFileBasename() + '.opf')
-        open(pathof(outopf), 'wb').write("".join(data))
-        if self.isK8:
-            return self.BookId
+            open(pathof(outopf), 'wb').write(data)
+
+
+    def writeK8OPF(self, has_obfuscated_fonts=False, epubver='2'):
+        # Write opf for mobi8
+        data = self.buildK8OPF(has_obfuscated_fonts, epubver)
+        outopf = os.path.join(self.files.k8oebps, OPF_NAME)
+        open(pathof(outopf), 'wb').write(data)
+        return self.BookId
+
+
+    def getBookId(self):
+        return self.BookId
+
+    def getNCXName(self):
+        return self.ncxname
+
+    def getNAVName(self):
+        return self.navname
+
+    def getEPUBVersion(self):
+        return self.epubver
+
+    def hasNCX(self):
+        return self.ncxname != None
+
+    def hasNAV(self):
+        return self.navname != None
+
+
+    def sovleEPUBVersion(self):
+        # Determine EPUB version from metadata and spine.
+        metadata = self.metadata
+        k8resc = self.k8resc
+        self.epubver = '2'
+        if 'true' == metadata.get('fixed-layout', [''])[0].lower():
+            self.epubver = '3'
+        elif 'rtl' in metadata.get(PPD_KEY, [''])[0].lower():
+            self.epubver = '3'
+        elif 'rl' in metadata.get(PWM_KEY, [''])[0].lower():
+            self.epubver = '3'
+        elif k8resc != None and k8resc.hasSpine():
+            self.epubver = k8resc.getEPUBVersion()
+        return self.epubver
+
+
+    def convertAmazonMetadataToEPUB3(self, metadata):
+        # Convert Amazon definded meta to epub3 meta.
+        overwrite_rendition_properties = True
+
+        insert_pos = taglist.find(metadata, BEGIN_INFO_ONLY)
+        if insert_pos == None:
+            insert_pos = taglist.find(metadata, '<!-- The following meta tags are just for information and will be ignored by mobigen/kindlegen.')
+        if insert_pos == None:
+            insert_pos = len(metadata) - 1
+        opf_metadata = metadata[:insert_pos]
+        opf_metadata_tail = metadata[insert_pos:]
+
+        isrc = taglist.find(opf_metadata, 'meta', 'name', 'fixed-layout')
+        if isrc != None:
+            fixedlayout = taglist.get_attrib(opf_metadata, isrc, 'content')
+            content = {'true' : 'pre-paginated'}.get(fixedlayout.lower(), 'reflowable')
+            idst = taglist.find(opf_metadata, 'meta', 'property', 'rendition:layout')
+            if idst == None:
+                newdata = '<meta property="rendition:layout">' + content + '</meta>\n'
+                opf_metadata.append(newdata)
+            elif overwrite_rendition_properties:
+                newdata = taglist.set_content(opf_metadata, idst, content)
+                opf_metadata[idst] = newdata
+
+        isrc = taglist.find(opf_metadata, 'meta', 'name', 'orientation-lock')
+        if isrc != None:
+            orientation = taglist.get_attrib(opf_metadata, isrc, 'content')
+            content = {'none' : 'auto'}.get(orientation.lower(), orientation)
+            idst = taglist.find(opf_metadata, 'meta', 'property', 'rendition:orientation')
+            if idst == None:
+                newdata = '<meta property="rendition:orientation">' + content + '</meta>\n'
+                opf_metadata.append(newdata)
+            elif overwrite_rendition_properties:
+                newdata = taglist.set_content(opf_metadata, idst, content)
+                opf_metadata[idst] = newdata
+
+        # Not certain whether corresponded or not.
+        isrc = taglist.find(opf_metadata, 'meta', 'name', 'original-resolution')
+        #if isrc != None:
+        if False:
+            resolution = taglist.get_attrib(opf_metadata, isrc, 'content')
+            re_resolution = re.compile(r'(?P<width>\d+)\D+(?P<height>\d+)', re.I)
+            mo_resolution = re_resolution.search(resolution)
+            if mo_resolution != None:
+                width = mo_resolution.group('width')
+                height = mo_resolution.group('height')
+                if int(width) > 0 and int(height) > 0:
+                    viewport = 'width={}, height={}'.format(width, height)
+            idst = taglist.find(opf_metadata, 'meta', 'property', 'rendition:viewport')
+            if idst == None:
+                newdata = '<meta property="rendition:viewport">' + viewport + '</meta>\n'
+                opf_metadata.append(newdata)
+            elif overwrite_rendition_properties:
+                newdata = taglist.set_content(opf_metadata, idst, viewport)
+                opf_metadata[idst] = newdata
+
+        opf_metadata += opf_metadata_tail
+        return opf_metadata
+
+
+    # XXX: Under construction.
+    def sovleRefineID(self):
+        metadata = self.metadata
+        dc_title = metadata.get('Title', [])
+        dc_creator = metadata.get('Creator', [])
+        dc_publisher = metadata.get('Publisher', [])
+        fileas_title = metadata.get('Title file-as', [])
+        fileas_creator = metadata.get('Creator file-as', [])
+        fileas_publisher = metadata.get('Publisher file-as', [])
+
+        title_ids = []
+        creator_ids = []
+        publisher_ids = []
+
+        k8resc = self.k8resc
+        if k8resc != None:
+            refineids = k8resc.metadata.getRefineIds()
+            for id_ in refineids:
+                if 'title' in id_.lower():
+                    title_ids.append(id_)
+            for id_ in refineids:
+                if 'creator' in id_.lower():
+                    creator_ids.append(id_)
+            for id_ in refineids:
+                if 'publisher' in id_.lower():
+                    publisher_ids.append(id_)
+
+        if len(dc_title) == 1:
+            if len(title_ids) == 1 and len(fileas_title) <= 1:
+                pass
+            elif len(title_ids) == 0 and len(fileas_title) == 1:
+                title_ids.append('title')
+            else:
+                title_ids = []
+        else:
+            title_ids = []
+
+        if len(dc_publisher) == 1:
+            if len(publisher_ids) == 1and len(fileas_publisher) <= 1:
+                pass
+            elif len(publisher_ids) == 0 and len(fileas_publisher) == 1:
+                publisher_ids.append('publisher')
+            else:
+                publisher_ids = []
+        else:
+            publisher_ids = []
+
+        if len(dc_creator) == 1:
+            if len(creator_ids) == 1 and len(fileas_creator) <= 1:
+                pass
+            elif len(creator_ids) == 0 and len(fileas_creator) == 1:
+                creator_ids.append('creator')
+            else:
+                creator_ids = []
+        else:
+            creator_ids = []
+
+        self.title_ids = title_ids
+        self.creator_ids = creator_ids
+        self.publisher_ids = publisher_ids
+        return
diff -u -N KindleUnpack_v072a/lib//mobi_taglist.py KindleUnpack_v072b/lib//mobi_taglist.py
--- KindleUnpack_v072a/lib//mobi_taglist.py	1970-01-01 09:00:00 +0900
+++ KindleUnpack_v072b/lib//mobi_taglist.py	2014-06-22 22:10:03 +0900
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""Functions to handle tag list.
+
+Supported structure is the list consist of single layer tags and comments such as:
+[<root>, '<child1/>', '<child2>text</child2>', '<!-- comment -->, '</root>'].
+"""
+
+import sys, os, re
+
+
+re_element = re.compile(r'''
+        (?P<comment><!--.*?-->)
+    |
+        (?P<start_tag><(?P<tag>[^\s/>]+)(.*?>|.*?(?P<empty>/>)))
+        (?(empty)|(?P<content>.*?)(?P<end_tag></(?P=tag)>))
+    ''', re.X|re.I|re.S)
+re_endws = re.compile(r'(?P<end_ws>\s*)(?P<end_bracket>/>|>)')
+
+
+def convert(src, tag=None):
+    # Convert to taglist from src string.
+    taglist = []
+    if tag != None:
+        pattern = '(?P<start_tag><{0:s}[^>]*>)(?P<child_elements>.*?)(?P<end_tag></{0:s}>)'.format(tag)
+        re_tag = re.compile(pattern, re.I|re.S)
+        mo_tag = re_tag.search(src)
+        if mo_tag != None:
+            start_tag = mo_tag.group('start_tag')
+            end_tag = mo_tag.group('end_tag')
+            elements = mo_tag.group('child_elements')
+            taglist.append(start_tag + '\n')
+        else:
+            elements = ''
+    else:
+        mo_tag = None
+        elements = src
+    pos = 0
+    mo_element = re_element.search(elements, pos)
+    while mo_element != None:
+        if mo_element.group('comment') != None:
+            taglist.append(mo_element.group())
+        elif mo_element.group('start_tag') != None:
+            taglist.append(mo_element.group() + '\n')
+        pos = mo_element.end()
+        mo_element = re_element.search(elements, pos)
+    if mo_tag != None:
+        taglist.append(end_tag + '\n')
+    return taglist
+
+
+def find(srclist, tag, attrib=None, value=None, start=0, end=None, indices=None):
+    # Find first index that given conditions match.
+    indices = findall(srclist, tag, attrib, value, 1, start, end, indices)
+    if len(indices) == 1:
+        return indices[0]
+    else:
+        return None
+
+def findall(srclist, tag, attrib=None, value=None, n=0, start=0, end=None, indices=None):
+    # Find indices that given conditions matches.
+    if indices == None:
+        if end == None:
+            end = len(srclist)
+        indices = range(start, end)
+
+    if tag[:3] == '!--':
+        pattern = r'(<{:})'.format(tag)
+    elif tag[:4] == '<!--':
+        pattern = r'({:})'.format(tag)
+    elif attrib == None:
+        pattern = r'<!--.*?-->|(<{:}\s+.*?>)'.format(tag)
+    elif value == None:
+        pattern = r'<!--.*?-->|(<{:}\s+.*?{:}.*?>)'.format(tag, attrib)
+    else:
+        pattern = r'<!--.*?-->|(<{:}\s+.*?{:}\s*=\s*"{:}".*?>)'.format(tag, attrib, value)
+    re_ = re.compile(pattern, re.S)
+
+    newindices = []
+    for i in indices:
+        mo = re_.search(srclist[i])
+        if mo != None and mo.group(1) != None:
+            newindices.append(i)
+            n -= 1
+            if n == 0:
+                break
+    return newindices
+
+def extract_tags(srclist, indices):
+    # Extract tags specified by indices
+    new_data = []
+    for i, item in enumerate(srclist):
+        if i in indices:
+            new_data.append(item)
+    return new_data
+
+def remove_tags(srclist, indices):
+    # Remove tags specified by indices
+    new_data = []
+    for i, item in enumerate(srclist):
+        if i not in indices:
+            new_data.append(item)
+    return new_data
+
+def remove_attrib(srclist, index, attrib):
+    # Return a tag whose specified attribute is removed.
+    pattern = r'\s+{:}\s*=\s*"(.*?)"'.format(attrib)
+    newdata = re.sub(pattern, '', srclist[index])
+    return newdata
+
+def get_attrib(srclist, index, attrib):
+    # Get specified attribute value.
+    pattern = r'\s+{:}\s*=\s*"(.*?)"'.format(attrib)
+    mo = re.search(pattern, srclist[index])
+    if mo != None:
+        return mo.group(1)
+    else:
+        return None
+
+def set_attrib(srclist, index, attrib, value):
+    # Return a tag whose specified attribute is added or replaced.
+    item = srclist[index]
+    repl = ' {:}="{:}"'.format(attrib, value)
+    pattern = r'(\s+{:}\s*=\s*".*?")|\s*(/?>)'.format(attrib)
+    mo = re.search(pattern, item)
+    if mo != None:
+        if mo.group(1) != None:
+            newitem = item[:mo.start()] + repl + item[mo.end():]
+        else:
+            newitem = item[:mo.start()] + repl + mo.group(2) + item[mo.end():]
+    return newitem
+
+
+def get_content(srclist, index):
+    # Get the content of specified tag.
+    item = srclist[index]
+    mo = re_element.search(item)
+    if mo != None:
+        return mo.group('content')
+    else:
+        return None
+    #re_tag = re.compile(r'(<.*?>)(.*?)(</.*>)')
+    #mo = re_tag.search(srclist[index])
+    #if mo != None:
+    #    return mo.group(2)
+    #else:
+    #    return None
+
+
+def set_content(srclist, index, value):
+    # Return a tag whose content is added or replaced.
+    item = srclist[index]
+    mo = re_element.search(item)
+    if mo != None:
+        start_tag = mo.group('start_tag')
+        mo_ws = re_endws.search(start_tag)
+        if mo_ws != None:
+            repl = '{:}>{:}</{:}>'.format(start_tag[:mo_ws.start()], value, mo.group('tag'))
+            newitem = item[:mo.start()] + repl + item[mo.end():]
+            return newitem
+    return item
