使用Python3将Markdown(.md)文本转换成 html、pdf

一、Markdown中不同的文本内容会分成不同的文本块,并通过markdown的语法控制进行文本的拼接,组成新的文件。

二、利用Python3实现(.md)文件转换成(.html)文件

  在cmd命令行下进入(.py)文件目录下,使用命令进行执行

  >python md2html.py <file.md> <file.html>

import sys, re

#生成器模块
def lines(file):
    #在文本最后加一空行
    for line in file: yield line
    yield ‘\n‘

def blocks(file):
    #生成单独的文本块
    block = []
    for line in lines(file):
        if line.strip():
            block.append(line)
        elif block:
            yield ‘‘.join(block).strip()
            block = []

#文本块处理程序
class Handler:
    """
    处理程序父类
    """
    def callback(self, prefix, name, *args):
        method = getattr(self, prefix + name, None)
        if callable(method): return method(*args)

    def start(self, name):
        self.callback(‘start_‘, name)

    def end(self, name):
        self.callback(‘end_‘, name)

    def sub(self, name):
        def substitution(match):
            result = self.callback(‘sub_‘, name, match)
            if result is None: result = match.group(0)
            return result
        return substitution

class HTMLRenderer(Handler):
    """
    HTML处理程序,给文本块加相应的HTML标记
    """
    def start_document(self):
        print(‘<html><head><title>Python文本解析</title></head><body>‘)

    def end_document(self):
        print(‘</body></html>‘)

    def start_paragraph(self):
        print(‘<p style="color: #444;">‘)

    def end_paragraph(self):
        print(‘</p>‘)

    def start_heading(self):
        print(‘<h2 style="color: #68BE5D;">‘)

    def end_heading(self):
        print(‘</h2>‘)

    def start_list(self):
        print(‘<ul style="color: #363736;">‘)

    def end_list(self):
        print(‘</ul>‘)

    def start_listitem(self):
        print(‘<li>‘)

    def end_listitem(self):
        print(‘</li>‘)

    def start_title(self):
        print(‘<h1 style="color: #1ABC9C;">‘)

    def end_title(self):
        print(‘</h1>‘)

    def sub_emphasis(self, match):
        return(‘<em>%s</em>‘ % match.group(1))

    def sub_url(self, match):
        return(‘<a target="_blank" style="text-decoration: none;color: #BC1A4B;" href="%s">%s</a>‘ % (match.group(1), match.group(1)))

    def sub_mail(self, match):
        return(‘<a style="text-decoration: none;color: #BC1A4B;" href="mailto:%s">%s</a>‘ % (match.group(1), match.group(1)))

    def feed(self, data):
        print(data)

#规则,判断每个文本块应该如何处理
class Rule:
    """
    规则父类
    """
    def action(self, block, handler):
        """
        加标记
        """
        handler.start(self.type)
        handler.feed(block)
        handler.end(self.type)
        return True

class HeadingRule(Rule):
    """
    一号标题规则
    """
    type = ‘heading‘
    def condition(self, block):
        """
        判断文本块是否符合规则
        """
        return not ‘\n‘ in block and len(block) <= 70 and not block[-1] == ‘:‘

class TitleRule(HeadingRule):
    """
    二号标题规则
    """
    type = ‘title‘
    first = True

    def condition(self, block):
        if not self.first: return False
        self.first = False
        return HeadingRule.condition(self, block)

class ListItemRule(Rule):
    """
    列表项规则
    """
    type = ‘listitem‘
    def condition(self, block):
        return block[0] == ‘-‘

    def action(self, block, handler):
        handler.start(self.type)
        handler.feed(block[1:].strip())
        handler.end(self.type)
        return True

class ListRule(ListItemRule):
    """
    列表规则
    """
    type = ‘list‘
    inside = False
    def condition(self, block):
        return True

    def action(self, block, handler):
        if not self.inside and ListItemRule.condition(self, block):
            handler.start(self.type)
            self.inside = True
        elif self.inside and not ListItemRule.condition(self, block):
            handler.end(self.type)
            self.inside = False
        return False

class ParagraphRule(Rule):
    """
    段落规则
    """
    type = ‘paragraph‘

    def condition(self, block):
        return True

class Code(Rule):
    ‘‘‘
    代码框规则
    高亮显示规则
    。。。
    ‘‘‘
    pass

# 对整个文本进行解析
class Parser:
    """
    解析器父类
    """
    def __init__(self, handler):
        self.handler = handler
        self.rules = []
        self.filters = []

    def addRule(self, rule):
        """
        添加规则
        """
        self.rules.append(rule)

    def addFilter(self, pattern, name):
        """
        添加过滤器
        """
        def filter(block, handler):
            return re.sub(pattern, handler.sub(name), block)
        self.filters.append(filter)

    def parse(self, file):
        """
        解析
        """
        self.handler.start(‘document‘)
        for block in blocks(file):
            for filter in self.filters:
                block = filter(block, self.handler)
            for rule in self.rules:
                if rule.condition(block):
                    last = rule.action(block, self.handler)
                    if last: break
        self.handler.end(‘document‘)

class BasicTextParser(Parser):
    """
    纯文本解析器
    """
    def __init__(self, handler):
        Parser.__init__(self, handler)
        self.addRule(ListRule())
        self.addRule(ListItemRule())
        self.addRule(TitleRule())
        self.addRule(HeadingRule())
        self.addRule(ParagraphRule())

        self.addFilter(r‘\*(.+?)\*‘, ‘emphasis‘)
        self.addFilter(r‘(http://[\.a-zA-Z/]+)‘, ‘url‘)
        self.addFilter(r‘([\.a-zA-Z][email protected][\.a-zA-Z]+[a-zA-Z]+)‘, ‘mail‘)

"""
运行测试程序
"""
handler = HTMLRenderer()
parser = BasicTextParser(handler)
parser.parse(sys.stdin)

三、利用Python3将文本转化成pdf文件

  命令>python md2pdf.py 源文件 目标文件 [options]

Options:
    -h --help     show help document.
    -v --version  show version information.
    -o --output   translate sourcefile into html file.
    -p --print    translate sourcefile into pdf file and html file respectively.
    -P --Print    translate sourcefile into pdf file only.
import os,re
import sys,getopt
from enum import Enum
from subprocess import call
from functools import reduce

from docopt import docopt

__version__ = ‘1.0‘

# 定义三个枚举类
# 定义表状态
class TABLE(Enum):
    Init = 1
    Format = 2
    Table = 3

# 有序序列状态
class ORDERLIST(Enum):
    Init = 1
    List = 2

# 块状态
class BLOCK(Enum):
    Init = 1
    Block = 2
    CodeBlock = 3

# 定义全局状态,并初始化状态
table_state = TABLE.Init
orderList_state = ORDERLIST.Init
block_state = BLOCK.Init
is_code = False
is_normal = True

temp_table_first_line = []
temp_table_first_line_str = ""

need_mathjax = False

def test_state(input):
    global table_state, orderList_state, block_state, is_code, temp_table_first_line, temp_table_first_line_str
    Code_List = ["python\n", "c++\n", "c\n"]

    result = input

    # 构建正则表达式规则
    # 匹配块标识
    pattern = re.compile(r‘```(\s)*\n‘)
    a = pattern.match(input)

    # 普通块
    if  a and block_state == BLOCK.Init:
        result = "<blockquote>"
        block_state = BLOCK.Block
        is_normal = False
    # 特殊代码块
    elif len(input) > 4 and input[0:3] == ‘```‘ and (input[3:9] == "python" or input[3:6] == "c++" or input[3:4]== "c") and block_state == BLOCK.Init:
        block_state = BLOCK.Block
        result = "<code></br>"
        is_code = True
        is_normal = False
    # 块结束
    elif block_state == BLOCK.Block and input == ‘```\n‘:
        if is_code:
            result = "</code>"
        else:
            result = "</blockquote>"
        block_state = BLOCK.Init
        is_code = False
        is_normal = False
    elif block_state == BLOCK.Block:
        pattern = re.compile(r‘[\n\r\v\f\ ]‘)
        result = pattern.sub("&nbsp", result)
        pattern = re.compile(r‘\t‘)
        result = pattern.sub("&nbsp" * 4, result)
        result = "<span>" + result + "</span></br>"
        is_normal = False

    # 解析有序序列
    if len(input) > 2 and input[0].isdigit() and input[1] == ‘.‘ and orderList_state == ORDERLIST.Init:
        orderList_state = ORDERLIST.List
        result = "<ol><li>" + input[2:] + "</li>"
        is_normal = False
    elif len(input) > 2 and  input[0].isdigit() and input[1] == ‘.‘ and orderList_state == ORDERLIST.List:
        result = "<li>" + input[2:] + "</li>"
        is_normal = False
    elif orderList_state == ORDERLIST.List and (len(input) <= 2 or input[0].isdigit() == False or input[1] != ‘.‘):
        result = "</ol>" + input
        orderList_state = ORDERLIST.Init

    # 解析表格
    pattern = re.compile(r‘^((.+)\|)+((.+))$‘)
    match = pattern.match(input)
    if match:
        l = input.split(‘|‘)
        l[-1] = l[-1][:-1]
        # 将空字符弹出列表
        if l[0] == ‘‘:
            l.pop(0)
        if l[-1] == ‘‘:
            l.pop(-1)
        if table_state == TABLE.Init:
            table_state = TABLE.Format
            temp_table_first_line = l
            temp_table_first_line_str = input
            result = ""
        elif table_state == TABLE.Format:
            # 如果是表头与表格主题的分割线
            if reduce(lambda a, b: a and b, [all_same(i,‘-‘) for i in l], True):
                table_state = TABLE.Table
                result = "<table><thread><tr>"
                is_normal = False

                # 添加表头
                for i in temp_table_first_line:
                    result += "<th>" + i + "</th>"
                result += "</tr>"
                result += "</thread><tbody>"
                is_normal = False
            else:
                result = temp_table_first_line_str + "</br>" + input
                table_state = TABLE.Init

        elif table_state == TABLE.Table:
            result = "<tr>"
            for i in l:
                result += "<td>" + i + "</td>"
            result += "</tr>"

    elif table_state == TABLE.Table:
        table_state = TABLE.Init
        result = "</tbody></table>" + result
    elif table_state == TABLE.Format:
        pass

    return result

# 判断 lst 是否全由字符 sym 构成 
def all_same(lst, sym):
    return not lst or sym * len(lst) == lst

# 处理标题
def handleTitle(s, n):
    temp = "<h" + repr(n) + ">" + s[n:] + "</h" + repr(n) + ">"
    return temp

# 处理无序列表
def handleUnorderd(s):
    s = "<ul><li>" + s[1:]
    s += "</li></ul>"
    return s

def tokenTemplate(s, match):
    pattern = ""
    if match == ‘*‘:
        pattern = "\*([^\*]*)\*"
    if match == ‘~~‘:
        pattern = "\~\~([^\~\~]*)\~\~"
    if match == ‘**‘:
        pattern = "\*\*([^\*\*]*)\*\*"
    return pattern

# 处理特殊标识,比如 **, *, ~~
def tokenHandler(s):
    l = [‘b‘, ‘i‘, ‘S‘]
    j = 0
    for i in [‘**‘, ‘*‘, ‘~~‘]:
        pattern = re.compile(tokenTemplate(s,i))
        match = pattern.finditer(s)
        k = 0
        for a in match:
            if a:
                content = a.group(1)
                x,y = a.span()
                c = 3
                if i == ‘*‘:
                    c = 5
                s = s[:x+c*k] + "<" + l[j] + ">" + content + "</" + l[j] + ">" + s[y+c*k:]
                k += 1
        pattern = re.compile(r‘\$([^\$]*)\$‘)
        a = pattern.search(s)
        if a:
            global need_mathjax
            need_mathjax = True
        j += 1
    return s

# 处理链接
def link_image(s):
    # 超链接
    pattern = re.compile(r‘\\\[(.*)\]\((.*)\)‘)
    match = pattern.finditer(s)
    for a in match:
        if a:
            text, url = a.group(1,2)
            x, y = a.span()
            s = s[:x] + "<a href=" + url + " target=\"_blank\">" + text + "</a>" + s[y:]

    # 图像链接
    pattern = re.compile(r‘!\[(.*)\]\((.*)\)‘)
    match = pattern.finditer(s)
    for a in match:
        if a:
            text, url = a.group(1,2)
            x, y = a.span()
            s = s[:x] + "<img src=" + url + " target=\"_blank\">" + "</a>" + s[y:]

    # 角标
    pattern = re.compile(r‘(.)\^\[([^\]]*)\]‘)
    match = pattern.finditer(s)
    k = 0
    for a in match:
        if a:
            sym,index = a.group(1,2)
            x, y = a.span()
            s = s[:x+8*k] + sym + "<sup>" + index + "</sup>" + s[y+8*k:]
        k += 1

    return s

def parse(input):
    global block_state, is_normal
    is_normal = True
    result = input

    # 检测当前 input 解析状态
    result = test_state(input)

    if block_state == BLOCK.Block:
        return result

    # 分析标题标记 #
    title_rank = 0
    for i in range(6, 0, -1):
        if input[:i] == ‘#‘*i:
            title_rank = i
            break
    if title_rank != 0:
        # 处理标题,转化为相应的 HTML 文本
        result = handleTitle(input, title_rank)
        return result

    # 分析分割线标记 --
    if len(input) > 2 and all_same(input[:-1], ‘-‘) and input[-1] == ‘\n‘:
        result = "<hr>"
        return result

    # 解析无序列表
    unorderd = [‘+‘, ‘-‘]
    if result != "" and result[0] in unorderd :
        result = handleUnorderd(result)
        is_normal = False

    f = input[0]
    count = 0
    sys_q = False
    while f == ‘>‘:
        count += 1
        f = input[count]
        sys_q = True
    if sys_q:
        result = "<blockquote style=\"color:#8fbc8f\"> "*count + "<b>" + input[count:] + "</b>" + "</blockquote>"*count
        is_normal = False

    # 处理特殊标记,比如 ***, ~~~
    result = tokenHandler(result)

    # 解析图像链接
    result = link_image(result)
    pa = re.compile(r‘^(\s)*$‘)
    a = pa.match(input)
    if input[-1] == "\n" and is_normal == True and not a :
        result+="</br>"

    return result 

def run(source_file, dest_file, dest_pdf_file, only_pdf):
    # 获取文件名
    file_name = source_file
    # 转换后的 HTML 文件名
    dest_name = dest_file
    # 转换后的 PDF 文件名
    dest_pdf_name = dest_pdf_file

    # 获取文件后缀
    _, suffix = os.path.splitext(file_name)
    if suffix not in [".md",".markdown",".mdown","mkd"]:
        print(‘Error: the file should be in markdown format‘)
        sys.exit(1)

    if only_pdf:
        dest_name = ".~temp~.html"

    f = open(file_name, "r")
    f_r = open(dest_name, "w")

    # 往文件中填写 HTML 的一些属性
    f_r.write("""<style type="text/css">div {display: block;font-family: "Times New Roman",Georgia,Serif}            #wrapper { width: 100%;height:100%; margin: 0; padding: 0;}#left { float:left;             width: 10%;  height: 100%;  }#second {   float:left;   width: 80%;height: 100%;               }#right {float:left;  width: 10%;  height: 100%;             }</style><div id="wrapper"> <div id="left"></div><div id="second">""")
    f_r.write("""<meta charset="utf-8"/>""")

    # 逐行解析 markdwon 文件
    for eachline in f:
        result = parse(eachline)
        if result != "":
            f_r.write(result)

    f_r.write("""</br></br></div><div id="right"></div></div>""")

    # 公式支持
    global need_mathjax
    if need_mathjax:
        f_r.write("""<script type="text/x-mathjax-config">        MathJax.Hub.Config({tex2jax: {inlineMath: [[‘$‘,‘$‘], [‘\\(‘,‘\\)‘]]}});        </script><script type="text/javascript"         src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>""")
    # 文件操作完成之后记得关闭!!!
    f_r.close()
    f.close()

    # 调用扩展 wkhtmltopdf 将 HTML 文件转换成 PDF
    if dest_pdf_name != "" or only_pdf:
        call(["wkhtmltopdf", dest_name, dest_pdf_name])
    # 如果有必要,删除中间过程生成的 HTML 文件
    if only_pdf:
        call(["rm", dest_name])

# 主函数
def main():
    dest_file = "translation_result.html"
    dest_pdf_file = "translation_result.pdf"

    only_pdf = False

    args = docopt(__doc__, version=__version__)

    dest_file = args[‘<outputfile>‘] if args[‘--output‘] else dest_file

    dest_pdf_file = args[‘<outputfile>‘] if args[‘--print‘] or args[‘--Print‘] else ""

    run(args[‘<sourcefile>‘], dest_file, dest_pdf_file, args[‘--Print‘])

if __name__=="__main__":
    main() 

原文地址:https://www.cnblogs.com/null-/p/10053532.html

时间: 2024-11-09 00:53:28

使用Python3将Markdown(.md)文本转换成 html、pdf的相关文章

将markdown (md)文件转换成带侧边栏目录(toc)的html文件

想用md记一些笔记,特别是一些日常需要快速查阅的命令或者手册, 因为用有道笔记之类的笔记软件,感觉过于庞大和笨重,用txt文件记录,格式上又不够清晰并且不能快速跳转.写成md,然后转成html似乎是个不错的选择. md转html的工具遍地都是,但是这些工具转成的html都没有侧边栏目录,除非这个html非常短,这样的html页面查阅起来就跟txt也区别不大了. 想找个简单的工具,竟然很难找到合适的.要么就是可以生成目录,但是在目录在页面顶部,一滚动就看不见了:要么,就是特别笨重和麻烦,比如给你一

文本转换成音频流

1.生成声音文件 DotNetSpeech.SpeechVoiceSpeakFlags SSF = DotNetSpeech.SpeechVoiceSpeakFlags.SVSFlagsAsync; DotNetSpeech.SpVoice vo = new SpVoiceClass(); System.Windows.Forms.SaveFileDialog SFD = new System.Windows.Forms.SaveFileDialog(); SFD.Filter = "All f

powershell 将文本转换成表格的另一种方式

$text=" 1 梦幻西游 216406 2014-01-21 资料片 2 炉石传说 15905 2014-01-24 公测 3 新大话西游 214465 2002-08-01 公测 4 问道 11322 2006-04-22 公测 5 沃土 10094 2014-04-23 内测 6 QQ仙灵 10056 2014-06-06 公测 7 神武 10011 2010-09-17 公测 8 大侠Q传 9576 2014-06-27 内测 9 女神三国 9343 2014-06-28 内测 10

将文本转换成语音

又一次在这里和大家在这里和大家见面了.几天给大家看的是如何实现将文本转换成语音.这可是现在很流行的一种快捷表达方式哦!!看<nikita>没?当时我的那个羡慕呀!!不过还好,有我们的讯飞科技为我们写这个强大的程序接口,这样,我们就有机会实现其效果哦! 这里只是谢了一个简单的类似与HelloWorld的那种简单程序.但是这个一通则百通吧. 首先呢?我们必须要在Eclipse环境里面导入一个jar包,那就是这个地址上的jar包.http://download.csdn.net/detail/wan

文本转换成语音

"让别人读书给你听",在某些情况下是一种需求.从程序员的角度来说,就是要把"人"换成"程序",让程序来朗读文字.满足这个需求的关键技术点,是"文本转换成语音",简称TTS. 这次介绍的,并不是TTS实现的原理,而是TTS的应用,也就是基于特定的库或开源项目的使用.对于原理的东西,小程在准备好理论的知识后再尝试讲解清楚. TTS的实现,有不少项目,这里介绍的是eSpeak. 本文介绍如何通过eSpeak来实现文本转换成语音的功能

文本转换成语音的免费工具

文本转换成语音的免费工具?日常生活中,我们在使用微信聊天工具的时候,如果不方便输入文字的情况下,我们会使用语音给对方发送消息.如果对方不方便听取语音消息的时候,可以将语音转换成文字,但是仅限于翻译普通话.那么,文字转换成语音,有没有好用靠谱的软件呢?小编给大家分享一个工具,可以将文字转换成语音.使用工具:迅捷PDF转换器1.首先大家可以在百度浏览器搜索关键词PDF转换器,然后将软件下载安装到电脑中.为下面的操作提前准备.2.软件安装后,鼠标双击进入工具操作页面,这个软件的色彩搭配很好,增强了视觉

python将文本转换成语音的代码

将写代码过程中经常用的一些代码片段备份一下,如下代码段是关于python将文本转换成语音的代码,应该是对小伙伴们有一些好处. # Text To Speech using SAPI (Windows) and Python module pyTTS by Peter Parente# download installer file pyTTS-3.0.win32-py2.4.exe # and pywin32-204.win32-py2.4.exe at this date the latest

扫描的图片如何转换成一个PDF文件

在扫描一些文件资料的时候会生成很多的图片,在整理这些图片时都会按照顺序将图片合成一个文件,一般是需要将这些图片转换成一个文档格式的,如果是需要发送给他人查看一般都会转为PDF文件格式,那么如何将这些扫描的图片转换成一个PDF文件呢? 首先尽量将每张图片调整成统一尺寸,并按照顺序为图片命名,方便后面的操作. 然后打开文件转换工具,在其他文件转pdf中选择“图片转PDF”选项.然后把整理好的图片按照顺序添加的转换工具的列表中,并查看下方将所有图片合成一个文件选项是否为“是”. 最后点开始转换,文件逐

怎么将DWG 格式CAD图纸转换成彩色PDF格式?

怎么将DWG 格式CAD图纸转换成彩色PDF格式?一般CAD设计师绘制好一张CAD图纸文件都是dwg格式的,那么我们会为了方便进行图纸的查看会将其进行各种格式的转换,今天小编就要来教大家的就是怎么将dwg格式的CAD图纸转换成彩色PDF格式的全部操作步骤,希望大家能够进行采纳! 步骤一:首先需要打开您电脑上面的CAD转换器软件,如果您电脑上面没有这款软件的话,您就需要去到官网上面进行下载安装了,小编在这里使用的就是这款"迅捷CAD转换器软件"!步骤二:将软件完成下载后将软件安装到你们的