#!/usr/bin/env python
# -*- coding: utf-8 -*-

# In EPUB3, NCX and <guide> MAY exist in OPF, although the NCX is superseded
# by the Navigation Document and the <guide> is deprecated. Currently, EPUB3_WITH_NCX
# and EPUB3_WITH_GUIDE are set to True due to compatibility with epub2 reading systems.
# They might be change to set to False in the future.
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. """

EPUB_OPF = '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. """

EXTH_TITLE_FURIGANA = 'Unknown_508'
""" The name for Title Furigana(similar to file-as) set by KDP. """

EXTH_CREATOR_FURIGANA = 'Unknown_517'
""" The name for Creator Furigana(similar to file-as) set by KDP. """

EXTH_PUBLISHER_FURIGANA = 'Unknown_522'
""" The name for Publisher Furigana(similar to file-as) set by KDP. """


import sys, os, re, uuid
from path import pathof
from datetime import datetime

from xml.sax.saxutils import escape as xmlescape
from HTMLParser import HTMLParser

EXTRA_ENTITIES = {'"':'&quot;', "'":"&apos;"}

class OPFProcessor(object):
    def __init__(self, files, metadata, fileinfo, imgnames, hasNCX, mh, usedmap, pagemapxml='', guidetext='', k8resc=None, epubver='2'):
        self.files = files
        self.metadata = metadata
        self.fileinfo = fileinfo
        self.imgnames = imgnames
        self.has_ncx = hasNCX
        self.codec = mh.codec
        self.isK8 = mh.isK8()
        self.printReplica = mh.isPrintReplica()
        self.guidetext = guidetext
        self.used = usedmap
        self.k8resc = k8resc
        self.covername = None
        self.cover_id = 'cover_img'
        if self.k8resc is not None and self.k8resc.cover_name is not None:
            # update cover id info from RESC if available
            self.cover_id = self.k8resc.cover_name
        self.h = HTMLParser()
        # Create a unique urn uuid
        self.BookId = str(uuid.uuid4())
        self.pagemap = pagemapxml
        self.ncxname = None
        self.navname = None

        # page-progression-direction is only set in spine
        self.page_progression_direction = metadata.pop('page-progression-direction', [None])[0]
        if 'rl' in metadata.get('primary-writing-mode', [''])[0]:
            self.page_progression_direction = 'rtl'
        self.epubver = epubver  # the epub version set by user
        self.target_epubver = epubver  # the epub vertion set by user or detected automatically
        if self.epubver == 'A':
            self.target_epubver = self.autodetectEPUBVersion()
        elif self.epubver == 'F':
            self.target_epubver = '2'
        elif self.epubver != '2' and self.epubver != '3':
            self.target_epubver = '2'

        # id for rifine attributes
        self.title_id = {}
        self.creator_id = {}
        self.publisher_id = {}
        # extra attributes
        self.title_attrib = {}
        self.creator_attrib = {}
        self.publisher_attrib = {}
        self.extra_attributes = [] # for force epub2 option
        # Create epub3 metadata from EXTH.
        self.exth_solved_refines_metadata = []
        self.exth_refines_metadata = []
        self.exth_fixedlayout_metadata = []

        self.defineRefinesID()
        self.processRefinesMetadata()
        if self.k8resc is not None:
            # Create metadata in RESC section.
            self.k8resc.createMetadata(epubver)
        if self.target_epubver == "3":
            self.createMetadataForFixedlayout()


    def escapeit(self, sval, EXTRAS=None):
        # note, xmlescape and unescape do not work with utf-8 bytestrings
        # so pre-convert to full unicode and then convert back since opf is utf-8 encoded
        uval = sval.decode('utf-8')
        if EXTRAS:
            ures = xmlescape(self.h.unescape(uval), EXTRAS)
        else:
            ures = xmlescape(self.h.unescape(uval))
        return ures.encode('utf-8')

    def createMetaTag(self, data, property, content, refid=''):
        refines = ''
        if refid:
            refines = ' refines="#%s"' % refid
        data.append('<meta property="%s"%s>%s</meta>\n' % (property, refines, content))


    def buildOPFMetadata(self, start_tag, has_obfuscated_fonts=False):
        # convert from EXTH metadata format to target epub version metadata
        # epub 3 will ignore <meta name="xxxx" content="yyyy" /> style metatags
        #    but allows them to be present for backwards compatibility
        #    instead the new format is
        #    <meta property="xxxx" id="iiii" ... > property_value</meta>
        #       and DCMES elements such as:
        #    <dc:blah id="iiii">value</dc:blah>

        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',
                                                'Updated Title', 'Font Signature (hex)', 'Tamper Proof Keys (hex)',  ]

        #def handleTag(data, metadata, key, tag, ids={}):
        def handleTag(data, metadata, key, tag, attrib={}):
            '''Format metadata values.

            @param data: List of formatted metadata entries.
            @param metadata: The metadata dictionary.
            @param key: The key of the metadata value to handle.
            @param tag: The opf tag corresponds to the metadata value.
            ###@param ids: The ids in tags for refines property of epub3.
            @param attrib: The extra attibute for refines or opf prefixs.
           '''
            if key in metadata.keys():
                for i, value in enumerate(metadata[key]):
                    closingTag = tag.split(" ")[0]
                    res = '<%s%s>%s</%s>\n' % (tag, attrib.get(i, ''), self.escapeit(value), closingTag)
                    data.append(res)
                del metadata[key]

        # these are allowed but ignored by epub3
        def handleMetaPairs(data, metadata, key, name):
            if key in metadata.keys():
                for value in metadata[key]:
                    res = '<meta name="%s" content="%s" />\n' % (name, self.escapeit(value, EXTRA_ENTITIES))
                    data.append(res)
                del metadata[key]


        data = []
        data.append(start_tag + '\n')
        # Handle standard metadata
        if 'Title' in metadata.keys():
            handleTag(data, metadata, 'Title', 'dc:title', self.title_attrib)
        else:
            data.append('<dc:title>Untitled</dc:title>\n')
        handleTag(data, metadata, 'Language', 'dc:language')
        if 'UniqueID' in metadata.keys():
            handleTag(data, metadata, 'UniqueID', 'dc:identifier id="uid"')
        else:
            # No unique ID in original, give it a generic one.
            data.append('<dc:identifier id="uid">0</dc:identifier>\n')

        if self.target_epubver == '3':
            # epub version 3 minimal metadata requires a dcterms:modifed date tag
            self.createMetaTag(data, 'dcterms:modified', datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"))

        if self.isK8 and has_obfuscated_fonts:
            # Use the random generated urn:uuid so obuscated fonts work.
            # It doesn't need to be _THE_ unique identifier to work as a key
            # for obfuscated fonts in Sigil, ADE and calibre. Its just has
            # to use the opf:scheme="UUID" and have the urn:uuid: prefix.
            data.append('<dc:identifier opf:scheme="UUID">urn:uuid:'+self.BookId+'</dc:identifier>\n')

        handleTag(data, metadata, 'Creator', 'dc:creator', self.creator_attrib)
        handleTag(data, metadata, 'Contributor', 'dc:contributor')
        handleTag(data, metadata, 'Publisher', 'dc:publisher', self.publisher_attrib)
        handleTag(data, metadata, 'Source', 'dc:source')
        handleTag(data, metadata, 'Type', 'dc:type')
        handleTag(data, metadata, 'ISBN', 'dc:identifier opf:scheme="ISBN"')
        if 'Subject' in metadata.keys():
            if 'SubjectCode' in metadata.keys():
                codeList = metadata['SubjectCode']
                del metadata['SubjectCode']
            else:
                codeList = None
            for i in range(len(metadata['Subject'])):
                if codeList and i < len(codeList):
                    data.append('<dc:subject BASICCode="'+codeList[i]+'">')
                else:
                    data.append('<dc:subject>')
                data.append(self.escapeit(metadata['Subject'][i])+'</dc:subject>\n')
            del metadata['Subject']
        handleTag(data, metadata, 'Description', 'dc:description')
        handleTag(data, metadata, 'Published', 'dc:date opf:event="publication"')
        handleTag(data, metadata, 'Rights', 'dc:rights')

        if self.epubver == 'F':
            if self.extra_attributes or k8resc is not None and k8resc.extra_attributes:
                data.append('<!-- THE FOLLOWINGS ARE REQUIRED TO INSERT INTO <dc:xxx> MANUALLY\n')
                if self.extra_attributes:
                    data += self.extra_attributes
                if k8resc is not None and k8resc.extra_attributes:
                    data += k8resc.extra_attributes
                data.append('-->\n')
        else:
            # Append refines metadata.
            if self.exth_solved_refines_metadata:
                data.append('<!-- Refines MetaData from EXTH -->\n')
                data += self.exth_solved_refines_metadata
            if self.exth_refines_metadata or k8resc is not None and k8resc.refines_metadata:
                data.append('<!-- THE FOLLOWINGS ARE REQUIRED TO EDIT IDS MANUALLY\n')
                if self.exth_refines_metadata:
                    data += self.exth_refines_metadata
                if k8resc is not None and k8resc.refines_metadata:
                    data += k8resc.refines_metadata
                data.append('-->\n')

        # Append metadata in RESC section.
        if k8resc is not None and k8resc.extra_metadata:
            data.append('<!-- Extra MetaData from RESC\n')
            data += k8resc.extra_metadata
            data.append('-->\n')


        if 'CoverOffset' in metadata.keys():
            imageNumber = int(metadata['CoverOffset'][0])
            self.covername = self.imgnames[imageNumber]
            if self.covername is None:
                print "Error: Cover image %s was not recognized as a valid image" % imageNumber
            else:
                # <meta name="cover"> is obsoleted in EPUB3, but kindlegen v2.9 requires it.
                data.append('<meta name="cover" content="' + self.cover_id + '" />\n')
                self.used[self.covername] = 'used'
            del metadata['CoverOffset']

        handleMetaPairs(data, metadata, 'Codec', 'output encoding')
        # handle kindlegen specifc tags
        handleTag(data, metadata, 'DictInLanguage', 'DictionaryInLanguage')
        handleTag(data, metadata, 'DictOutLanguage', 'DictionaryOutLanguage')
        handleMetaPairs(data, metadata, 'RegionMagnification', 'RegionMagnification')
        handleMetaPairs(data, metadata, 'book-type', 'book-type')
        handleMetaPairs(data, metadata, 'zero-gutter', 'zero-gutter')
        handleMetaPairs(data, metadata, 'zero-margin', 'zero-margin')
        handleMetaPairs(data, metadata, 'primary-writing-mode', 'primary-writing-mode')
        handleMetaPairs(data, metadata, 'fixed-layout', 'fixed-layout')
        handleMetaPairs(data, metadata, 'orientation-lock', 'orientation-lock')
        handleMetaPairs(data, metadata, 'original-resolution', 'original-resolution')

        # these are not allowed in epub2 or 3 so convert them to meta name content pairs
        # perhaps these could better be mapped into the dcterms namespace instead
        handleMetaPairs(data, metadata, 'Review', 'review')
        handleMetaPairs(data, metadata, 'Imprint', 'imprint')
        handleMetaPairs(data, metadata, 'Adult', 'adult')
        handleMetaPairs(data, metadata, 'DictShortName', 'DictionaryVeryShortName')

        # these are needed by kobo books upon submission but not sure if legal metadata in epub2 or epub3
        if 'Price' in metadata.keys() and 'Currency' in metadata.keys():
            priceList = metadata['Price']
            currencyList = metadata['Currency']
            if len(priceList) != len(currencyList):
                print "Error: found %s price entries, but %s currency entries."
            else:
                for i in range(len(priceList)):
                    data.append('<SRP Currency="'+currencyList[i]+'">'+priceList[i]+'</SRP>\n')
            del metadata['Price']
            del metadata['Currency']

        if self.target_epubver == '3':
            # Append metadata for EPUB3.
            if self.exth_fixedlayout_metadata:
                data.append('<!-- EPUB3 MedaData converted from EXTH -->\n')
                data += self.exth_fixedlayout_metadata

        # all that remains is extra EXTH info we will store inside a comment inside meta name/content pairs
        # so it can not impact anything and will be automatically stripped out if found again in a RESC section
        data.append(BEGIN_INFO_ONLY + '\n')
        if 'ThumbOffset' in metadata.keys():
            imageNumber = int(metadata['ThumbOffset'][0])
            imageName = self.imgnames[imageNumber]
            if imageName is None:
                print "Error: Cover Thumbnail image %s was not recognized as a valid image" % imageNumber
            else:
                data.append('<meta name="Cover ThumbNail Image" content="'+ 'Images/'+imageName+'" />\n')
                # self.used[imageName] = 'used' # thumbnail image is always generated by Kindlegen, so don't include in manifest
                self.used[imageName] = 'not used'
            del metadata['ThumbOffset']
        for metaName in META_TAGS:
            if metaName in metadata.keys():
                for value in metadata[metaName]:
                    data.append('<meta name="'+metaName+'" content="'+self.escapeit(value, EXTRA_ENTITIES)+'" />\n')
                del metadata[metaName]
        for key in metadata.keys():
            for value in metadata[key]:
                data.append('<meta name="'+key+'" content="'+self.escapeit(value, EXTRA_ENTITIES)+'" />\n')
            del metadata[key]
        data.append(END_INFO_ONLY + '\n')
        data.append('</metadata>\n')
        return data


    def buildOPFManifest(self, ncxname, navname=None):
        # buildManifest for mobi7, azw4, epub2 and epub3.
        k8resc = self.k8resc
        cover_id = self.cover_id
        hasK8RescSpine = k8resc is not None and k8resc.hasSpine()
        self.ncxname = ncxname
        self.navname = navname

        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', # for mobi7
                '.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 = []

        idcnt = 0
        for [key,dir,fname] in self.fileinfo:
            name, ext = os.path.splitext(fname)
            ext = ext.lower()
            media = media_map.get(ext)
            ref = "item%d" % idcnt
            if hasK8RescSpine:
                if key is not None and key in k8resc.spine_idrefs.keys():
                    ref = k8resc.spine_idrefs[key]
            properties = ''
            if dir != '':
                fpath = dir + '/' + fname
            else:
                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 is not 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 self.target_epubver == '3':
                        properties = 'properties="cover-image"'
                else:
                    ref = "item%d" % idcnt
                if ext == '.ttf' or ext == '.otf':
                    if self.isK8: # fonts are only used in Mobi 8
                        fpath = 'Fonts/' + fname
                        data.append('<item id="{0:}" media-type="{1:}" href="{2:}" {3:}/>\n'.format(ref, media, fpath, properties))
                else:
                    fpath = 'Images/' + fname
                    data.append('<item id="{0:}" media-type="{1:}" href="{2:}" {3:}/>\n'.format(ref, media, fpath, properties))
                idcnt += 1

        if self.target_epubver == '3' and navname is not None:
            data.append('<item id="nav" media-type="application/xhtml+xml" href="Text/' + navname + '" properties="nav"/>\n')
        if self.has_ncx and ncxname is not None:
            data.append('<item id="ncx" media-type="application/x-dtbncx+xml" href="' + ncxname +'" />\n')
        if self.pagemap != '':
            data.append('<item id="map" media-type="application/oebs-page-map+xml" href="page-map.xml" />\n')
        data.append('</manifest>\n')
        return [data, spinerefs]


    def buildOPFSpine(self, spinerefs, isNCX):
        # build spine
        k8resc = self.k8resc
        hasK8RescSpine = k8resc is not None and k8resc.hasSpine()
        data = []
        ppd = ''
        if self.isK8 and self.page_progression_direction is not None:
            ppd = ' page-progression-direction="{:s}"'.format(self.page_progression_direction)
        ncx = ''
        if isNCX:
            ncx = ' toc="ncx"'
        map=''
        if self.pagemap != '':
            map = ' page-map="map"'
        if self.epubver == 'F':
            if ppd:
                ppd = '<!--' + ppd + ' -->'
            spine_start_tag = '<spine{1:s}{2:s}>{0:s}\n'.format(ppd, map, ncx)
        else:
            spine_start_tag = '<spine{0:s}{1:s}{2:s}>\n'.format(ppd, map, ncx)
        data.append(spine_start_tag)

        if hasK8RescSpine:
            for key in k8resc.spine_order:
                idref = k8resc.spine_idrefs[key]
                attribs = k8resc.spine_pageattributes[key]
                tag = '<itemref idref="%s"' % idref
                for aname, val in attribs.items():
                    if self.epubver == 'F' and aname == 'properties':
                        continue
                    if val is not None:
                        tag += ' %s="%s"' % (aname, val)
                tag += '/>'
                if self.epubver == 'F' and 'properties' in attribs:
                    val = attribs['properties']
                    if val is not None:
                        tag += '<!-- properties="%s" -->' % val
                tag += '\n'
                data.append(tag)
        else:
            start = 0
            # special case the created coverpage if need be
            [key, dir, fname] = self.fileinfo[0]
            if key is not None and key == "coverpage":
                entry = spinerefs[start]
                data.append('<itemref idref="%s" linear="no"/>\n' % entry)
                start += 1
            for entry in spinerefs[start:]:
                data.append('<itemref idref="' + entry + '"/>\n')
        data.append('</spine>\n')
        return data


    def buildMobi7OPF(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.has_ncx:
            ncxname = self.files.getInputFileBasename() + '.ncx'
        else:
            ncxname = None
        [opf_manifest, spinerefs] = self.buildOPFManifest(ncxname)
        data += opf_manifest
        opf_spine = self.buildOPFSpine(spinerefs, self.has_ncx)
        data += opf_spine
        data.append('<tours>\n</tours>\n')
        if not self.printReplica:
            guide ='<guide>\n' + self.guidetext + '</guide>\n'
            data.append(guide)
        data.append('</package>\n')
        return ''.join(data)


    def buildEPUBOPF(self, has_obfuscated_fonts=False):
        print "Building an opf for mobi8 using epub version: ", self.target_epubver
        if self.target_epubver == '2':
            has_ncx = self.has_ncx
            has_guide = True
            ncxname = None
            ncxname = TOC_NCX
            navname = None
            package = '<package version="2.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="uid">\n'
            tours = '<tours>\n</tours>\n'
        else:
            has_ncx = EPUB3_WITH_NCX
            has_guide = EPUB3_WITH_GUIDE
            ncxname = None
            if has_ncx:
                ncxname = TOC_NCX
            navname = NAVIGATION_DOCUMENT
            package = '<package version="3.0" xmlns="http://www.idpf.org/2007/opf" prefix="rendition: http://www.idpf.org/vocab/rendition/#" unique-identifier="uid">\n'
            tours = ''
        data = []
        data.append('<?xml version="1.0" encoding="utf-8"?>\n')
        data.append(package)
        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.buildOPFManifest(ncxname, navname)
        data += opf_manifest
        opf_spine = self.buildOPFSpine(spinerefs, has_ncx)
        data += opf_spine
        data.append(tours)
        if has_guide:
            guide ='<guide>\n' + self.guidetext + '</guide>\n'
            data.append(guide)
        data.append('</package>\n')
        return ''.join(data)


    def writeOPF(self, has_obfuscated_fonts=False):
        if self.isK8:
            data = self.buildEPUBOPF(has_obfuscated_fonts)
            outopf = os.path.join(self.files.k8oebps, EPUB_OPF)
            open(pathof(outopf), 'wb').write(data)
            return self.BookId
        else:
            data = self.buildMobi7OPF()
            outopf = os.path.join(self.files.mobi7dir, self.files.getInputFileBasename() + '.opf')
            open(pathof(outopf), 'wb').write(data)
            return 0


    def getBookId(self):
        return self.BookId

    def getNCXName(self):
        return self.ncxname

    def getNAVName(self):
        return self.navname

    def getEPUBVersion(self):
        return self.target_epubver

    def hasNCX(self):
        return self.ncxname is not None and self.has_ncx

    def hasNAV(self):
        return self.navname is not None

    def autodetectEPUBVersion(self):
        # Determine EPUB version from metadata and RESC.
        metadata = self.metadata
        k8resc = self.k8resc
        epubver = '2'
        if 'true' == metadata.get('fixed-layout', [''])[0].lower():
            epubver = '3'
        elif metadata.get('orientation-lock', [''])[0].lower() in ['portrait', 'landscape']:
            epubver = '3'
        elif self.page_progression_direction == 'rtl':
            epubver = '3'
        elif k8resc is not None and k8resc.needEPUB3():
            epubver = '3'
        return epubver


    def defineRefinesID(self):
        # the following EXTH are set by KDP.
        # 'Title_Furigana_(508)'
        # 'Creator_Furigana_(517)',
        # 'Publisher_Furigana_(522)'
        # It is difficult to find correspondence between Title, Creator, Publisher
        # and EXTH 508,512, 522 if they have more than two values since KDP seems not preserve the oders of EXTH 508,512 and 522.
        # It is also difficult to find correspondence between them and tags which have refine attributes in RESC.
        # So editing manually is required.
        metadata = self.metadata

        needRefinesId = False
        if self.k8resc is not None:
            needRefinesId = self.k8resc.hasRefines()
        # Create id for rifine attributes
        if (needRefinesId or EXTH_TITLE_FURIGANA in metadata.keys()) and 'Title' in metadata.keys():
          for i in range(len(metadata.get('Title'))):
                self.title_id[i] = 'title%02d' % (i+1)

        if (needRefinesId or EXTH_CREATOR_FURIGANA in metadata.keys()) and 'Creator' in metadata.keys():
            for i in range(len(metadata.get('Creator'))):
                self.creator_id[i] = 'creator%02d' % (i+1)

        if (needRefinesId or EXTH_PUBLISHER_FURIGANA in metadata.keys()) and 'Publisher' in metadata.keys():
            for i in range(len(metadata.get('Publisher'))):
                self.publisher_id[i] = 'publisher%02d' % (i+1)


    def processRefinesMetadata(self):
        # create refines metadata defined in epub3 or convert refines property to opf: attribues for epub2.
        metadata = self.metadata

        refines_list = [
                [EXTH_TITLE_FURIGANA, self.title_id, self.title_attrib, 'title00'],
                [EXTH_CREATOR_FURIGANA, self.creator_id, self.creator_attrib, 'creator00'],
                [EXTH_PUBLISHER_FURIGANA, self.publisher_id, self.publisher_attrib, 'publisher00']
                ]

        create_refines_metadata = False
        for EXTH in zip(*refines_list)[0]:
            if EXTH in metadata.keys():
                create_refines_metadata = True
                break
        if create_refines_metadata:
            for [EXTH, id, attrib, defaultid] in refines_list:
                if self.target_epubver == '3':
                    for i, value in id.items():
                        attrib[i] = ' id="%s"' % value

                    if EXTH in metadata.keys():
                        if len(metadata[EXTH]) == 1 and len(id) == 1:
                            self.createMetaTag(self.exth_solved_refines_metadata, 'file-as', metadata[EXTH][0], id[0])
                        else:
                            for i, value in enumerate(metadata[EXTH]):
                                self.createMetaTag(self.exth_refines_metadata, 'file-as', value, id.get(i, defaultid))
                else:
                    if len(metadata[EXTH]) == 1 and len(id) == 1:
                        attr = ' opf:file-as="%s"' % metadata[EXTH][0]
                        attrib[0] = attr
                    else:
                        for i, value in enumerate(metadata[EXTH]):
                            attr = ' id="#%s" opf:file-as="%s"\n' % (id.get(i, defaultid), value)
                            self.extra_attributes.append(attr)


    def createMetadataForFixedlayout(self):
        # convert fixed layout to epub3 format if needed.
        metadata = self.metadata

        if 'fixed-layout' in metadata.keys():
            fixedlayout = metadata['fixed-layout'][0]
            content = {'true' : 'pre-paginated'}.get(fixedlayout.lower(), 'reflowable')
            self.createMetaTag(self.exth_fixedlayout_metadata, 'rendition:layout', content)

        if 'orientation-lock' in metadata.keys():
            content = metadata['orientation-lock'][0].lower()
            if content == 'portrait' or content == 'landscape':
                self.createMetaTag(self.exth_fixedlayout_metadata, 'rendition:orientation', content)

        # according to epub3 spec about correspondence with Amazon
        # if 'original-resolution' is provided it needs to be converted to
        # meta viewport property tag stored in the <head></head> of **each**
        # xhtml page - so this tag would need to be handled by editing each part
        # before reaching this routine
        # we need to add support for this to the k8html routine
        # if 'original-resolution' in metadata.keys():
        #     resolution = metadata['original-resolution'][0].lower()
        #     width, height = resolution.split('x')
        #     if width.isdigit() and int(width) > 0 and height.isdigit() and int(height) > 0:
        #         viewport = 'width=%s, height=%s' % (width, height)
        #         self.createMetaTag(self.exth_fixedlayout_metadata, 'rendition:viewport', viewport)
