个人向word文档编辑器(一)

 

最近在空闲时想做一些东西,之前一直想做一个自己的日志记录工具, 然后需要增加文字、图片,可以播放本地音乐等一些功能。

目前实现了文字、图片的编辑和储存(数据储存,之后的打开可以不依赖本地图片数据);

现在将一些代码和思路想法记录下,分享给需要的小伙伴们

1、目前的主要界面如下

上面是菜单栏,工具栏暂时还没有添加。

下面是由一个QTableView和QTextEdit组成的主体部分。之后应该会添加一个音乐播放器的控件在下面

插入一段布局的代码

setStretch是用来控制控件在布局内的拉伸大小的

另外这里不能直接对MainWindow调用this.setLayout()

    hboxLayout->setStretch(0, 1);
    hboxLayout->setStretch(2, 2.5);
    //this->setLayout(hboxLayout);不能直接set,显示不出来并且提示错误
    //Attempting to set QLayout "" on MainWindow "", which already has a layout

    QWidget *newWidget = new QWidget(this);
    newWidget->setLayout(hboxLayout);
    this->setCentralWidget(newWidget);
View Code

 

二、文字和图片数据的保存方式

最开始做的时候,以为将文字和图片从QTextEdit通过toPlainText()接口读出来然后保存到一个文件,显示的时候直接readAll()然后丢给控件显示就完了。

后来发现还是太天真。文字数据和图片数据是两种数据,并且QTextEdit是按照富文本格式去处理数据的,通过QTextDocument 的toHtml获取到控件的数据,发现是按照HTML格式编辑的,其中图片已这样的格式保存

<img width="400" height="274" src="C:/Users/Default/AppData/myAppImg/11/IMG_000000011.jpg"/>

也就是显示的时候通过src路径去加载,如果删除本地的图片就显示不了。

 

所以定下的方案是,将输出数据和其中加载的图片数据组合,保存到自定义数据中,在打开的时候根据规则进行解析,还原成HTML格式文字数据和对应的图片数据。

首先对输出的控件数据中图片路径修改为指向定义的“处理空间”路径,这个处理空间,就是在打开文件时生成的图片备份文件和文字备份文件,这样在控件加载文字数据时,会加载备份的图片,就可以显示了。

对文字数据和图片数据,进行了格式编码。以下是自己定的格式说明 ^-^

 

数据项

格式定义

长度

TAG值

数据格式

说明

文件头

文件头

4

B

标识一个自定义的文件类型。

clog

 

文件长度

4

B

整个数据条目的长度

0x00  0x00  0x00  0x24

文字编码

    文件保存路径

1

0xC1

B

标识文件数据解压后的文件路径

 

文件数据

1

0XC2

B

文件的数据流数据

数据编码

图片保存路径

1

0xD1

B

图片格式

jpg、png、bmp

 

 

图片数据

1

0xD2

B

Base64格式图片数据

备用

 

1

0xE1

B

 

 其所有数据都按照TLV(标签-长度-值)据组合,其中长度域有单独设置。

(1)对原文件数据的修改,主要是图片的路径修改

void MainWindow::saveFile(const QString &ht, int row)
{
    QTextDocument *textDocument = m_textEdit->document();
    QDomDocument dcreateDoc;
    if(!ht.isEmpty()){
        if(!dcreateDoc.setContent(ht)){
            qDebug() << "str html set Content error";
        }
    }else{
        if(!dcreateDoc.setContent(textDocument->toHtml())){
            qDebug() << "document html set Content error";
        }
    }

    //这个地方有想过直接修改获取QString字符串,但是我修改之后尝试获取新的字符串是没有被改过的,所以这里用了一个文件来保存在读取
    //最外层节点
    QDomElement roots = dcreateDoc.documentElement();
    QDomNodeList list = roots.childNodes();
    //analyImg会更改图片的路径指向备份路径
    QByteArray imgBuff = analyImg(list);

    QString path = qApp->applicationDirPath() + "/copy.htm";
    QFile file(path);
    if(!file.open(QIODevice::ReadWrite)){
        qDebug() << "open copy file error";
    }
    QTextStream out(&file);
    dcreateDoc.save(out,4);
    file.close();//调用file.flush()不能刷新

    file.open(QIODevice::ReadOnly);
    QByteArray html = file.readAll();
    file.close();
    file.remove();

    //组文件和图片数据
    QString fil;
    if(row != tag){
        fil = m_fileHand->getFileName(row);
    }else{
        fil = m_fileHand->getFileName(tag);
    }

    fil = fil.mid(0, fil.indexOf("."));
    QString newFileName = BACKSAVE_PATH + tr("/%1/%2").arg(fil).arg(m_fileHand->getFileName(tag));
    QByteArray all,all1;
    //增加文件标识域 "clog"
    all.append(tr("clog").toLatin1());
    //储存数据域
    all1.append(tlvByte(0xC1, newFileName.toLatin1()));
    all1.append(tlvByte(0xC2, html));
    all1.append(imgBuff);
    //增加数据长度域
    QString len = QString("%1").arg(all1.length(), 8, 16, QChar('0'));
    all.append(QByteArray::fromHex(len.toLatin1()));
    all.append(all1);
    //串->clog+数据长度域+数据域
    if(!m_fileHand->save(all, row)){
        QMessageBox::warning(NULL, "warning",
                             "save file falt.", QMessageBox::Ok);
    }
}

QByteArray MainWindow::analyImg(QDomNodeList &list)
{
    //要保证传入的list不为空
    if(list.isEmpty()){
        return 0;
    }
    QString imgpath;
    QByteArray img;//储存所有的图片的信息
    for(int i = 0;i < list.length(); i++){
        if(list.at(i).toElement().tagName() == "img"){
            //获取src指向的图片路径
            imgpath = list.at(i).toElement().attributeNode("src").value();
            QFileInfo file(imgpath);
            QString path = m_fileHand->getFileName(tag);
            path = path.mid(0, path.indexOf("."));
            QString newFile = BACKSAVE_PATH +tr("/%1/%2").arg(path).arg(file.fileName());

            //替换图片路径
            list.at(i).toElement().setAttribute("src",newFile);
            //qDebug() << "替换之后路径:" <<  list.at(i).toElement().attributeNode("src").value();
            //tlvByte是用来组tlv格式的数据处理函数
            img.append(tlvByte(0xD1,newFile.toLatin1()));
            //用readAll读到的图片数据
            QByteArray imgByte = imgDataByte(imgpath);
            img.append(tlvByte(0xD2,imgByte));
        }
        QDomNodeList it = list.at(i).childNodes();
        if(it.length() >= 1){
            //回调,一层一层处理完所有图片数据
            img += analyImg(it);
        }else{
            continue;
        }
    }
    return img;
}    
View Code

(2)最后获得的数据串,需要经过压缩在进行写入,本次用到的是Qt自带的Zlib库

(3)通过readAll读出文件数据,需要解出各标签数据在写入各个文件。

QByteArray CFileHandle::analyByte(const QByteArray &byte)
{
    if(byte.isEmpty()){
        return "";
    }
    QByteArray analy;
    QString str;
    analy = byte.mid(0,4);
    if(!("clog" == str.prepend(analy))){
        qDebug() << "it's not app Standard file";
        return "";
    }

    analy = byte.mid(4,4);

    int byteLen = analy.toHex().toInt(NULL, 16);
    analy = byte.mid(8);//取所有文件数据
    //长度不一样也进行解析
    if(analy.length() != byteLen)
        qDebug() << "file byte len error";

    QString filepath = analyTLV(analy);//返回文本文件路径

    QFile file(filepath);
    if(file.exists()){
        if(file.open(QIODevice::ReadOnly)){
            analy = file.readAll();
        }
        file.close();
    }

    return analy;
}

//解出文件路径返回,并将所有标签数据解出写入对应文件中
QString CFileHandle::analyTLV(const QByteArray &tlvByte)
{
    QString file;
    QString str;
    QList<QString> listStr;
    QList<QByteArray> listByte;

    QByteArray byteT;
    QByteArray byteL;
    QByteArray byteV;
    QByteArray Llen;
    int len = 0;
    int length = 0;

    for(int i = 0; i < tlvByte.length(); ){
        //T
        byteT = tlvByte.mid(i,1);
        i += 1;

        byteL = tlvByte.mid(i, 1);
        //如果L的最高位为1,则剩下的位值表示长度
        if((byteL.at(0) & 0x80) == 0x80){
            len = byteL.at(0) & 0x7f;
            i += 1;
            Llen = tlvByte.mid(i, len);
            //数据长度
            length = Llen.toHex().toInt(NULL, 16);
            i += len;
        }else{
            length = byteL.at(0) & 0xff;
            i += 1;

        }

        //V 数据
        byteV = tlvByte.mid(i, length);//数据
        str = "";

        i += length;//循环i不用自增

        //每一个C1 D1后都跟着C2 D2,确保数据连贯,则两个list可以对应下标
        if((byteT.at(0) & 0xff) == 0xC1){
            file = file.prepend(byteV);
            listStr.append(str.prepend(byteV));
        }else if((byteT.at(0) & 0xff) == 0xD1){
            listStr.append(str.prepend(byteV));
        }else if((byteT.at(0) & 0xff) == 0xC2
                 || (byteT.at(0) & 0xff) == 0xD2){
            listByte.append(byteV);
        }else
            qDebug() << "analy tlv error, byteT is:" << byteT.toHex()
                     << " byteV is:" << byteV;
    }

    //将所有文件创建
    QString filepath;
    for(int j = 0; j < listStr.length(); j++){
        filepath = listStr.at(j);       
        //创建文件名文件夹,防止同名文件导致的加载错误
        QString fp = filepath.mid(0, filepath.lastIndexOf("/"));
        QFileInfo fl(fp);
        if(!fl.exists()){
            QDir dir;
            dir.mkpath(fp);
        }

        QFile file(filepath);
        if(!file.exists()){//文件存在不创建
            //qDebug() << tr("j is %1").arg(j) << " filepath:" << filepath;
            if(file.open(QIODevice::WriteOnly)){
                file.write(listByte.at(j));
                file.close();
            }else{
                qDebug() << "create file error, file path:" <<
                            filepath;
            }
        }
    }

    if(file.isEmpty()){
        qDebug() << "dont't have file path,please check!";
        return "";
    }

    return file;
}
View Code

 

今天写到这里,之后在继续补充。

 

放假回来了,继续折腾这个玩意儿。

测试发现,在保存时使用的dcreateDoc.save(out, 4);接口,会格式化保存的htm文件,按照子节点的层级对每层子节点增加4个空格在开始;如果改为0则不会增加。

但是改为0的时候,在显示图片的时候,会将"text-indent:0px;">        <img src="C:" 这样的字符串转为"text-indent:0px;"><img src="C:" ,则当单独对一张图片进行位置移动,保存会格式化,图片一直顶格。

所以后面排除了通过QDomDocument来处理字符串替换路径的操作。直接对QTextEdit获取的串替换img的路径然后对这个串保存。更加简洁。

新代码如下:

 

void MainWindow::saveFile(int row, const QString &ht)
{

    QTextDocument *textDocument = m_textEdit->document();
    //qDebug() << "save str :" << textDocument->toHtml();
      QString htmlStr;
    if(ht.isEmpty()){
        htmlStr = textDocument->toHtml();
    }else{
        htmlStr = ht;
    }
    QByteArray imgBuff = analyXml(htmlStr, row);
    QByteArray html = htmlStr.toUtf8();
    //组文件和图片数据
    QString fil;
    fil = m_fileHand->getFileName(row);

    fil = fil.mid(0, fil.indexOf("."));
    QString newFileName = BACKSAVE_PATH + tr("/%1/%2").arg(fil).arg(fil) + ".html";
    QByteArray all,all1;
    //增加文件标识域 "clog"
    all.append(tr("clog").toLatin1());
    //储存数据域
    all1.append(tlvByte(0xC1, newFileName.toLatin1()));
    all1.append(tlvByte(0xC2, html));
    all1.append(imgBuff);
    //增加数据长度域
    QString len = QString("%1").arg(all1.length(), 8, 16, QChar('0'));
    all.append(QByteArray::fromHex(len.toLatin1()));
    all.append(all1);
    //串->clog+数据长度域+数据域

    if(!m_fileHand->save(all, row)){
        QMessageBox::warning(NULL, "warning",
                             "save file falt.", QMessageBox::Ok);
    }
}

QByteArray MainWindow::analyXml(QString &xml, int &row)
{
    if(xml.isEmpty())
        return "";
    QList<QString> xmlList = xml.split('\n');
    QString imgTag;
    QString leftTag;
    QString rightTag;
    QString imgpath;
    QString str;

    QByteArray img;//储存所有的图片的信息
    xml.clear();
    for(int i = 0; i < xmlList.length(); i++){
        int tag = xmlList.at(i).indexOf("src");
        if(tag > 0){//如果有图片存在
            leftTag = xmlList.at(i).mid(0,tag + 5);//第一段
            imgTag = xmlList.at(i).mid(tag + 5);//第二段

            tag = imgTag.indexOf('"');
            imgpath = imgTag.mid(0, tag);
            rightTag = imgTag.mid(tag);
            //qDebug() << "path:" << imgpath;

            QFileInfo file(imgpath);
            QString path = m_fileHand->getFileName(row);
            path = path.mid(0, path.indexOf("."));
            QString newFile = BACKSAVE_PATH +tr("/%1/%2").arg(path).arg(file.fileName());

            str = leftTag + newFile + rightTag;
            xml += str;
            //qDebug() << "str string is:" << str;

            img.append(tlvByte(0xD1,newFile.toLatin1()));
            QByteArray imgByte = imgDataByte(imgpath);
            img.append(tlvByte(0xD2,imgByte));
        }else{
            xml += xmlList.at(i);
        }
    }
    //qDebug() << "save xml is:" << xml;
    return img;
}
View Code

 

posted @ 2019-04-30 17:12  蓦然而然  阅读(1189)  评论(0编辑  收藏  举报