Flutter 創(chuàng)建多圖像的 PDF 文件

貓哥說

保持熱情去改變!

今天這篇文章是讓你在客戶端完成 PDF 的創(chuàng)建址否,這樣能減輕服務器的壓力。

這是很有必要的搜囱,服務器的 CPU 資源很寶貴霎肯。

Flutter 插件 https://pub.dev/packages/pdf

  • 功能有:
    • 載入圖片
    • 寫上文字
    • 加密、簽名文件
    • 也可以載入 pdf

老鐵記得 轉發(fā) 慢哈,貓哥會呈現更多 Flutter 好文~~~~

微信群 ducafecat

b 站 https://space.bilibili.com/404904528

原文

https://medium.com/flutterdevs/flutter-create-pdf-files-with-multiple-images-4458e813fe37

代碼

https://github.com/flutter-devs/flutter_pdf_create_view_demo

參考

正文

[圖片上傳失敗...(image-104616-1623982135598)]

在 Flutter 不同的功能使您的應用程序豐富的有用性蔓钟,并給簡單的客戶端做東西內的應用程序和改善客戶端的經驗,是一個專家合作是另外必不可少的開發(fā)人員卵贱。

有很多軟件包可以用來在應用程序中打開 pdf滥沫,有些比較復雜,有些并不難執(zhí)行键俱,在這里我將闡明可能最容易使用的方法兰绣。

在這個博客中,我們將探索 Flutter ー創(chuàng)建多張圖片的 PDF 文件编振。我們將實施一個演示程序缀辩,以顯示如何 Flutter 創(chuàng)建一個 pdf 文件與多個圖像使用的三個要素包在您的 Flutter 應用程序。

狀態(tài)管理

簡介:

PDF 很可能是用于交換業(yè)務信息的最著名的文檔格式,因為內容不能像不同配置那樣有效地更改雌澄。這樣可以保護我們的信息不受未經批準的更改的影響。一旦你知道了策略杯瞻,這通常是一個簡單的互動镐牺,我會告訴你在你的任務中制作 pdf 文檔的最好方法。

對于這個演示魁莉,需要三個基本的軟件包睬涧。

演示模塊:

[圖片上傳失敗...(image-fef52e-1623982135598)]

這個演示視頻顯示了如何在一個 Flutter 與多個圖像創(chuàng)建 pdf 文件。它顯示了 pdf 文件將如何使用這三個軟件包在您的 Flutter 應用程序旗唁。它顯示當用戶點擊一個創(chuàng)建按鈕畦浓,然后出現 pdf,根據頁面有多個圖像检疫。它會顯示在你的設備上讶请。

實施方案:

  • 第一步: 添加依賴項

將依賴項添加到 pubspec ー yaml 文件。

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  path_provider: ^2.0.1
  pdf: ^3.3.0
  syncfusion_flutter_pdfviewer: ^19.1.64-beta
  • 第二步: 添加 assets

將 assets 添加到 pubspec ー yaml 文件屎媳。

assets:
  - assets/images/
  • 第三步: 導入
import 'package:pdf/pdf.dart';
import 'package:path_provider/path_provider.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';
  • 第四步: 在應用程序的根目錄中運行 flutter 軟件包

如何實現 dart 文件中的代碼:

你需要分別在你的代碼中實現它:

在 lib 文件夾中創(chuàng)建一個名為 pdf _ screen _ demo. dart 的新 dart 文件夺溢。

  • 首先,讓我們創(chuàng)建一個基本的 PDF 文件:

在文件中創(chuàng)建一個 StatefulWidget烛谊,名為 PdfScreenDemo风响。

String pdfFile = '';

一個基本的用戶界面,我們有一個凸起的按鈕丹禀,使我們的 PDF 文件和一個可見性小部件揭示 PDF 瀏覽器一旦 PDF 記錄状勤。要查看 PDF 記錄,我們將使用 syncfusion/flutter/pdfviewer 包的 SfPdfViewer.file 小部件双泪,在這個小部件中持搜,我們將制作一個文檔,其方式與我們制作的 PDF 類似焙矛。

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
      Visibility(
          visible: pdfFile.isNotEmpty,
          child: SfPdfViewer.file(File(pdfFile),
              canShowScrollHead: false, canShowScrollStatus:
           false),
        ),
    RaisedButton(
        color: Colors.tealAccent,
        onPressed: () {
        },
        child: Text('Create a Pdf File')),
  ],
),

PDF 包有它自己的小部件庫存朵诫,為了調用這些庫存,我們需要導入 PDF 小部件作為一個變量名為 pw薄扁。

import 'package:pdf/widgets.dart' as pw;

為了構造一個 PDF 格式剪返,我們將通過調用 pw < widgetnme > 來調用小部件。

為了保存一個 pdf 文件邓梅,我們應該做一個脱盲。文件()。這個不常見的小部件將保存已生成 PDF 的數據日缨,因此我們在 _ PdfScreenDemoState 中生成一個變量如何钱反。

var pdf = pw.Document();

Lest’s create createPdfFile() method:

在這個模型中,我們的想法是制作一個多頁的帶有 a4 設計的 PDF。因為我們要添加的圖片的數量面哥,頁面的數量可能是多個的哎壳,按照這些順序,現在制作一個多頁面小部件是個好兆頭尚卫。

在 MultiPage 小部件的構建方法內部归榕,將有一個帶有 Header 的 Column 和 Divider,用于將實質內容與 Header 分離吱涉。在接下來的幾個階段中刹泄,我們將在本欄內的分隔符下添加實質內容。

createPdfFile() {
  pdf.addPage(pw.MultiPage(
      margin: pw.EdgeInsets.all(10),
      pageFormat: PdfPageFormat.a4,
      build: (pw.Context context) {
        return <pw.Widget>[
          pw.Column(
              crossAxisAlignment: pw.CrossAxisAlignment.center,
              mainAxisSize: pw.MainAxisSize.min,
              children: [
                pw.Text('Create a basic PDF',
                    textAlign: pw.TextAlign.center,
                    style: pw.TextStyle(fontSize: 26)),
                pw.Divider(),
              ]),
        ];
      }));
}

此后怎爵,我們應該創(chuàng)建一個 savePdfFile ()方法特石。

它是一種異步方法,等待 IOS 或 Android 平臺的目錄鳖链。然后姆蘸,該技術使用 ID 的名稱和目錄的 documentPath 創(chuàng)建一個記錄。你可以用任何你喜歡的方式來命名你的記錄芙委,但是對于不同的 PDF 創(chuàng)建來說乞旦,更聰明的做法是為每個 PDF 文檔都起一個特殊的名字。

最后题山,該方法將 PDF 保存為文件兰粉。Path 和 value 被賦給 setState 策略中的 pdfFile 變量,以便稍后刷新 UI顶瞳。

savePdfFile() async {
Directory documentDirectory = await getApplicationDocumentsDirectory();

String documentPath = documentDirectory.path;

String id = DateTime.now().toString();

File file = File("$documentPath/$id.pdf");

file.writeAsBytesSync(await pdf.save());
setState(() {
pdfFile = file.path;
pdf = pw.Document();
});
}

重要提示: 任命民主黨人是根本玖姑。在保存 pdf 文件之后,將 Document ()轉換為 pdf 變量慨菱。如果不這樣做焰络,該 pdf 將嘗試使另一個最近制作的 pdf 文件,這將導致一個有缺陷的 pdf 文件被制作符喝。

如果您需要保存您的 PDF 文件的字節(jié)闪彼,您可以利用下方的方法。

List<int> pdfBytes;
pdfBytes = await file.readAsBytes();
pdfFile base64Encode(pdfBytes);

除此之外协饲,使用異步 onPressed 方法:

為了保存記錄畏腕,我們需要首先等待 createPdfFile ()策略。

onPressed: () async {
  await createPdfFile();
  savePdfFile();
},

好了茉稠,現在一切都安排好了描馅。你可以點擊“創(chuàng)建一個 PDF 文件”按鈕來查看你的第一個基本 PDF 文檔。當我們運行應用程序時而线,我們應該得到屏幕的輸出铭污,就像下面的屏幕截圖一樣恋日。

image
  • 讓我們用一個圖片創(chuàng)建一個 pdf 文件:

在 pdf 包中,你可以添加一個圖片嘹狞。MemoryImage.因此岂膳,我們需要將圖片更改為內存字節(jié)。

首先導入下面的軟件包磅网。

import 'dart:typed_data';
import 'package:flutter/services.dart';

從那時起谈截,將 createdffile 轉換為一個 async 方法,并添加這兩個變量知市。

final ByteData bytes = await rootBundle.load('assets/images/null_safety.png');
final Uint8List byteList = bytes.buffer.asUint8List();

第一個變量在資源圖片上更改為字節(jié),第二個變量在字節(jié)上更改為 Uint8List 字節(jié)列表速蕊。

然后嫂丙,在這一點上添加一個 pw 圖像小部件在分隔符下面。

createPdfFile() async {
  final ByteData bytes =
      await rootBundle.load('assets/images/null_safety.png');
  final Uint8List byteList = bytes.buffer.asUint8List();
  pdf.addPage(pw.MultiPage(
      margin: pw.EdgeInsets.all(10),
      pageFormat: PdfPageFormat.a4,
      build: (pw.Context context) {
        return <pw.Widget>[
          pw.Column(
              crossAxisAlignment: pw.CrossAxisAlignment.center,
              mainAxisSize: pw.MainAxisSize.min,
              children: [
                pw.Text('Flutter Pdf File with Image',
                    textAlign: pw.TextAlign.center,
                    style: pw.TextStyle(fontSize: 26)),

                pw.Divider(),
              ]),
          pw.SizedBox(height: 70),
          pw.Image(
              pw.MemoryImage(
                byteList,
              ),
              height: 300,
              fit: pw.BoxFit.fitHeight),
        ];
      }));
}

MemoryImage 接受 byteList 作為位置競爭规哲,將圖片傳送到 pdf 記錄中「。現在重新啟動應用程序,并嘗試制作另一個 PDF 文檔唉锌,以查看其中包含圖片的文件隅肥。當我們運行應用程序時,我們應該獲得屏幕輸出袄简,就像下面的屏幕截圖一樣腥放。

image
  • 讓我們創(chuàng)建一個有多個圖片的 pdf 文件:

在演示的最后一步,我們將制作一個 pdf 構建技術绿语,它可以用給定的各種圖像生成一條記錄秃症。首先,我們需要制定一個策略吕粹,將圖片轉換為 Uint8List 設計种柑。

在狀態(tài)小部件中,創(chuàng)建一個空白列表匹耕,用于將圖片放入 Uint8list聚请。

List<Uint8List> imagesUint8list = [];

然后,我們應該重構 createdffile 方法稳其,并集中于另一種獲取圖片字節(jié)的技術驶赏。

getImageBytes(String assetImage) async {
    final ByteData bytes = await rootBundle.load(assetImage);
    final Uint8List byteList = bytes.buffer.asUint8List();
    imagesUint8list.add(byteList);
  }

現在我們將制作一個類型為 pw 的列表。createPdfFile ()技術中的小部件既鞠,它生成具有圖像標題和圖像本身的 Column母市。

final List<pw.Widget> pdfImages = imagesUint8list.map((image) {
      return pw.Padding(
          padding: pw.EdgeInsets.symmetric(vertical: 20, horizontal:    10),
          child: pw.Column(
              crossAxisAlignment: pw.CrossAxisAlignment.center,
              mainAxisSize: pw.MainAxisSize.max,
              children: [
                pw.Text(
                    'Image'
                            ' ' +
                        (imagesUint8list
                                    .indexWhere((element) => element
                                   == image) +
                                1)
                            .toString(),
                    style: pw.TextStyle(fontSize: 22)),
                pw.SizedBox(height: 10),
                pw.Image(
                    pw.MemoryImage(
                      image,
                    ),
                    height: 400,
                    fit: pw.BoxFit.fitHeight)
              ]));
    }).toList();

注意: 這是緊急給一個特定的最大大小的圖片小部件,否則一個圖片可能會溢出頁面設計促使一個失敗的 pdf 創(chuàng)建损趋。

Createdffile 最初會創(chuàng)建一個 for 循環(huán)患久,將多個圖片更改為 Uint8List椅寺。稍后,它將制作一個帶有頭部的圖片列表蒋失。最后返帕,pdfImages 將作為子文件顯示在 pdf 的基本列中。

createPdfFile() async {
    for (String image in assetImages) await getImageBytes(image);
    final List<pw.Widget> images = imagesUint8list.map((image) {
      return pw.Padding(
          padding: pw.EdgeInsets.symmetric(vertical: 20, horizontal:  10),
          child: pw.Column(
              crossAxisAlignment: pw.CrossAxisAlignment.center,
              mainAxisSize: pw.MainAxisSize.max,
              children: [
                pw.Text(
                    'Image'
                            ' ' +
                        (imagesUint8list
                                   .indexWhere((element) => element
                             == image) +
                                1)
                            .toString(),
                    style: pw.TextStyle(fontSize: 22)),
                pw.SizedBox(height: 10),
                pw.Image(
                    pw.MemoryImage(
                      image,
                    ),
                    height: 400,
                    fit: pw.BoxFit.fitHeight)
              ]));
    }).toList();
    pdf.addPage(pw.MultiPage(
        margin: pw.EdgeInsets.all(10),
        pageFormat: PdfPageFormat.a4,
        build: (pw.Context context) {
          return <pw.Widget>[
            pw.Column(
                crossAxisAlignment: pw.CrossAxisAlignment.center,
                mainAxisSize: pw.MainAxisSize.min,
                children: [
                  pw.Text('Create a Simple PDF',
                      textAlign: pw.TextAlign.center,
                      style: pw.TextStyle(fontSize: 26)),
                  pw.Divider(),
                ]),
            pw.Column(
                crossAxisAlignment: pw.CrossAxisAlignment.center,
                mainAxisSize: pw.MainAxisSize.max,
                children: pdfImages),
          ];
        }));
  }

另一個步驟是用 SingleChildScrollView 和 Expanded 小部件包裝可見性小部件篙挽。

Expanded(
              child: SingleChildScrollView(
                child: Visibility(
                  visible: pdfFile.isNotEmpty,
                  child: SfPdfViewer.file(File(pdfFile),
                      canShowScrollHead: false, canShowScrollStatus: false),
                ),
              ),
            ),

現在如何我們按下按鈕最后一次機會荆萤,使 PDF 文件與多個圖片。當我們運行應用程序時铣卡,我們應該得到屏幕的輸出链韭,就像下面的屏幕截圖一樣。

image

代碼:

import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';

class PdfScreenDemo extends StatefulWidget {
  @override
  _PdfScreenDemoState createState() => _PdfScreenDemoState();
}

class _PdfScreenDemoState extends State<PdfScreenDemo> {
  String pdfFile = '';
  var pdf = pw.Document();

  static const List<String> assetImages = [
    'assets/images/null_safety.png',
    'assets/images/stream.png',
    'assets/images/error_handling.jpg'
  ];
  List<Uint8List> imagesUint8list = [];

  @override
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Expanded(
                child: SingleChildScrollView(
                  child: Visibility(
                    visible: pdfFile.isNotEmpty,
                    child: SfPdfViewer.file(File(pdfFile),
                        canShowScrollHead: false, canShowScrollStatus: false),
                  ),
                ),
              ),
              RaisedButton(
                  color: Colors.tealAccent,
                  onPressed: () async {
                    await createPdfFile();
                    savePdfFile();
                  },
                  child: Text('Create a Pdf File')),
            ],
          ),
        ),
      ),
    );
  }

  getImageBytes(String assetImage) async {
    final ByteData bytes = await rootBundle.load(assetImage);
    final Uint8List byteList = bytes.buffer.asUint8List();
    imagesUint8list.add(byteList);
    print(imagesUint8list.length);
  }

  createPdfFile() async {
    for (String image in assetImages) await getImageBytes(image);
    final List<pw.Widget> pdfImages = imagesUint8list.map((image) {
      return pw.Padding(
          padding: pw.EdgeInsets.symmetric(vertical: 50, horizontal: 10),
          child: pw.Column(
              crossAxisAlignment: pw.CrossAxisAlignment.center,
              mainAxisSize: pw.MainAxisSize.max,
              children: [
                pw.Text(
                    'Image'
                            ' ' +
                        (imagesUint8list
                                    .indexWhere((element) => element == image) +
                                1)
                            .toString(),
                    style: pw.TextStyle(fontSize: 22)),
                pw.SizedBox(height: 30),
                pw.Image(
                    pw.MemoryImage(
                      image,
                    ),
                    height: 300,
                    fit: pw.BoxFit.fitHeight)
              ]));
    }).toList();

    pdf.addPage(pw.MultiPage(
        margin: pw.EdgeInsets.all(10),
        pageFormat: PdfPageFormat.a4,
        build: (pw.Context context) {
          return <pw.Widget>[
            pw.Column(
                crossAxisAlignment: pw.CrossAxisAlignment.center,
                mainAxisSize: pw.MainAxisSize.min,
                children: [
                  pw.Text('Flutter Pdf File with Multiple Image',
                      textAlign: pw.TextAlign.center,
                      style: pw.TextStyle(fontSize: 26)),
                  pw.Divider(),
                ]),
            pw.Column(
                crossAxisAlignment: pw.CrossAxisAlignment.center,
                mainAxisSize: pw.MainAxisSize.max,
                children: pdfImages),
          ];
        }));
  }

  savePdfFile() async {
    Directory documentDirectory = await getApplicationDocumentsDirectory();

    String documentPath = documentDirectory.path;

    String id = DateTime.now().toString();

    File file = File("$documentPath/$id.pdf");

    file.writeAsBytesSync(await pdf.save());
    setState(() {
      pdfFile = file.path;
      pdf = pw.Document();
    });
  }
}

Conclusion

在這篇文章中煮落,我解釋了創(chuàng)建 PDF 文件的基本結構與多重圖像 Flutter; 您可以修改這個代碼根據您的選擇敞峭。這是一個小的介紹創(chuàng)建 PDF 文件與多圖像用戶交互從我這邊,它的工作使用 Flutter蝉仇。

我希望這個博客將提供您在嘗試創(chuàng)建 PDF 文件與多個圖像在您的撲項目充分的信息旋讹。我們將向您展示介紹是什么?.制作一個演示程序為工作創(chuàng)建 PDF 文件與多個圖像和顯示當用戶點擊一個創(chuàng)建按鈕轿衔,然后 PDF 發(fā)生沉迹,與多個圖像根據頁面使用所有三個軟件包在您的 Flutter 應用程序。所以請嘗試一下害驹。


? 貓哥

https://ducafecat.tech/

https://github.com/ducafecat

往期

開源

GetX Quick Start

https://github.com/ducafecat/getx_quick_start

新聞客戶端

https://github.com/ducafecat/flutter_learn_news

strapi 手冊譯文

https://getstrapi.cn

微信討論群 ducafecat

系列集合

譯文

https://ducafecat.tech/categories/%E8%AF%91%E6%96%87/

開源項目

https://ducafecat.tech/categories/%E5%BC%80%E6%BA%90/

Dart 編程語言基礎

https://space.bilibili.com/404904528/channel/detail?cid=111585

Flutter 零基礎入門

https://space.bilibili.com/404904528/channel/detail?cid=123470

Flutter 實戰(zhàn)從零開始 新聞客戶端

https://space.bilibili.com/404904528/channel/detail?cid=106755

Flutter 組件開發(fā)

https://space.bilibili.com/404904528/channel/detail?cid=144262

Flutter Bloc

https://space.bilibili.com/404904528/channel/detail?cid=177519

Flutter Getx4

https://space.bilibili.com/404904528/channel/detail?cid=177514

Docker Yapi

https://space.bilibili.com/404904528/channel/detail?cid=130578

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末鞭呕,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子宛官,更是在濱河造成了極大的恐慌琅拌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摘刑,死亡現場離奇詭異进宝,居然都是意外死亡,警方通過查閱死者的電腦和手機枷恕,發(fā)現死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門党晋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人徐块,你說我怎么就攤上這事未玻。” “怎么了胡控?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵扳剿,是天一觀的道長。 經常有香客問我昼激,道長庇绽,這世上最難降的妖魔是什么锡搜? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮瞧掺,結果婚禮上耕餐,老公的妹妹穿的比我還像新娘。我一直安慰自己辟狈,他們只是感情好肠缔,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布冠绢。 她就那樣靜靜地躺著陡鹃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪征候。 梳的紋絲不亂的頭發(fā)上壹蔓,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天趟妥,我揣著相機與錄音,去河邊找鬼庶溶。 笑死煮纵,一個胖子當著我的面吹牛懂鸵,可吹牛的內容都是我干的偏螺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼匆光,長吁一口氣:“原來是場噩夢啊……” “哼套像!你這毒婦竟也來了?” 一聲冷哼從身側響起终息,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤夺巩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后周崭,有當地人在樹林里發(fā)現了一具尸體柳譬,經...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年续镇,在試婚紗的時候發(fā)現自己被綠了美澳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡摸航,死狀恐怖制跟,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情酱虎,我是刑警寧澤雨膨,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站读串,受9級特大地震影響聊记,放射性物質發(fā)生泄漏撒妈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一甥雕、第九天 我趴在偏房一處隱蔽的房頂上張望踩身。 院中可真熱鬧,春花似錦社露、人聲如沸挟阻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽附鸽。三九已至,卻和暖如春瞒瘸,著一層夾襖步出監(jiān)牢的瞬間坷备,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工情臭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留省撑,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓俯在,卻偏偏與公主長得像竟秫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子跷乐,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內容