Cấu trúc và định kiểu văn bản

Trong API Trang trình bày, văn bản có thể nằm trong các hình dạng hoặc trong các ô của bảng. Trước khi có thể thao tác và tạo kiểu cho văn bản, bạn cần hiểu cấu trúc và cách hoạt động của kiểu văn bản.

Trang này mô tả cách trình bày văn bản trong API Trang trình bày.

Trình tự các phần tử văn bản

Văn bản chứa trong một hình dạng hoặc ô trong bảng được tạo thành từ một chuỗi các cấu trúc TextElement. Trình tự này thể hiện cấu trúc của văn bản, theo thứ tự xuất hiện từ đầu đến cuối.

Ví dụ: hãy xem xét nội dung của trang trình bày này — tất cả nằm trong một hộp văn bản:

ảnh chụp màn hình của một trang trình bày đơn giản

Trang trình bày ở trên có một hộp văn bản, trong đó trường text chứa một trình tự các phần tử văn bản như minh hoạ trong sơ đồ sau:

sơ đồ hiển thị một chuỗi các thành phần văn bản

Cụ thể hơn, chuỗi văn bản này được biểu thị trong API Trang trình bày như sau:

"textElements": [ {
    "endIndex": 224,
    "paragraphMarker": { "style": {} }
  }, {
    "endIndex": 130,
    "textRun": { "content": "Li lingues differe in li grammatica e li vocabules. Omnicos directe al desirabilite de un nov ", "style": {} }
  }, {
    "endIndex": 143,
    "startIndex": 130,
    "textRun": { "content": "lingua franca", "style": { "italic": True } }
  }, {
    "endIndex": 224,
    "startIndex": 143,
    "textRun": { "content": ": solmen va esser necessi far:\n", "style": {} }
  }, {
    "endIndex": 243,
    "startIndex": 224,
    "paragraphMarker": {
      "style": { "indentStart": { "magnitude": 36, "unit": "PT" }, "direction": "LEFT_TO_RIGHT", "indentFirstLine": { "magnitude": 18, "unit": "PT" }, "spacingMode": "COLLAPSE_LISTS" },
      "bullet": { "listId": "foo123", "glyph": "\u25cf" }
    }
  }, {
    "endIndex": 243,
    "startIndex": 224,
    "textRun": { "content": "uniform grammatica\n", "style": {} }
  }, {
    "endIndex": 257,
    "startIndex": 243,
    "paragraphMarker": {
        "style": { "indentStart": { "magnitude": 36, "unit": "PT" }, "direction": "LEFT_TO_RIGHT", "indentFirstLine": { "magnitude": 18, "unit": "PT" }, "spacingMode": "COLLAPSE_LISTS" },
        "bullet": { "listId": "foo123", "glyph": "\u25cf" }
    }
}, {
    "endIndex": 257,
    "startIndex": 243,
    "textRun": { "content": "Pronunciation\n", "style": {} }
}, {
    "endIndex": 277,
    "startIndex": 257,
    "paragraphMarker": {
        "style": { "indentStart": { "magnitude": 36, "unit": "PT" }, "indentFirstLine": { "magnitude": 18, "unit": "PT" }, "spacingMode": "COLLAPSE_LISTS" },
        "bullet": { "listId": "foo123", "glyph": "\u25cf" }
    }
}, {
    "endIndex": 277,
    "startIndex": 257,
    "textRun": { "content": "plu sommun paroles.\n", "style": {} }
}, {
    "endIndex": 500,
    "startIndex": 277,
    "paragraphMarker": { "style": {} }
}, {
    "endIndex": 500,
    "startIndex": 277,
    "textRun": { "content": "Ka swu thefognay, tay waddeant varpa u inzo.\n", "style": {} }
}]

Nội dung TextElement

Mỗi thành phần văn bản đều chứa chỉ mục bắt đầuchỉ mục kết thúc dựa trên 0. Chỉ mục này mô tả vị trí của thành phần trong văn bản đầy đủ của thành phần trang, cùng với một trong các loại đối tượng văn bản sau:

Loại văn bản Nội dung mô tả
ParagraphMarker Phần tử văn bản này đại diện cho phần đầu của một đoạn mới. Chỉ mục đầu và cuối của thành phần văn bản thể hiện toàn bộ đoạn, bao gồm cả ký tự dòng mới kết thúc đoạn. Một đoạn văn không bao giờ chồng lên một đoạn khác. Đoạn luôn kết thúc bằng ký tự dòng mới, do đó luôn có một dòng mới ở cuối nội dung văn bản của một hình dạng hoặc ô trong bảng.

Đoạn văn có thể thuộc danh sách có dấu đầu dòng hoặc được đánh số. Nếu có, nội dung của trường ParagraphMarker.bullet sẽ bao gồm một mã nhận dạng danh sách. Mã nhận dạng này tham chiếu đến một phần tử danh sách tồn tại bên trong TextContent cùng với trình tự TextElement. Các đoạn trong cùng một danh sách logic sẽ tham chiếu đến cùng một mã danh sách.
TextRun Phần tử văn bản này đại diện cho một chuỗi văn bản liền kề và tất cả đều có cùng một kiểu văn bản. Văn bản chạy không bao giờ vượt qua ranh giới đoạn: ngay cả khi văn bản kết thúc một đoạn có cùng kiểu với văn bản bắt đầu đoạn tiếp theo, nội dung sẽ được tách sau ký tự dòng mới để tạo thành các lần chạy văn bản riêng biệt.

Nếu bạn cần xử lý chuỗi văn bản đầy đủ trong một phần tử trang, hãy lặp lại qua tất cả các phần tử văn bản, nối các chuỗi tìm được trong tất cả các lần chạy văn bản.
AutoText Văn bản tự động dùng để chỉ những vị trí trong văn bản tự động thay đổi tuỳ thuộc vào ngữ cảnh. Trong Trang trình bày, số này dùng để biểu thị số trang trình bày hiện tại bên trong văn bản.

Sửa đổi nội dung văn bản

Khi cần sửa đổi văn bản bằng API Trang trình bày, bạn không cần phải tạo tất cả các thành phần văn bản thích hợp một cách rõ ràng. Thay vào đó, bạn có thể thao tác trên văn bản như trong trình chỉnh sửa Trang trình bày: bằng cách chèn văn bản, xoá dải ô và cập nhật kiểu trên dải ô. Các thao tác này ngầm tạo các phần tử ParagraphMarkerTextRun nếu cần để phản ánh các thay đổi của bạn.

Chèn văn bản

Bạn có thể chèn văn bản vào một chỉ mục bằng cách sử dụng yêu cầu InsertTextRequest trong lệnh gọi đến batchUpdate. Trường insertionIndex của phương thức này chỉ định vị trí chèn văn bản; bạn có thể tính toán chỉ mục này bằng cách sử dụng các trường chỉ mục bắt đầu và kết thúc bên trong phần tử văn bản.

Tính năng chèn văn bản có một số tác dụng phụ phản ánh hành vi của trình chỉnh sửa Trang trình bày:

  • Việc chèn ký tự dòng mới ngầm tạo một đoạn văn bản mới, tạo thành phần văn bản ParagraphMarker bắt đầu tại chỉ mục của dòng mới và kết thúc ở dòng mới sau. Kiểu đoạn văn bản (bao gồm cả dấu đầu dòng và thông tin chi tiết về danh sách) sẽ được sao chép từ đoạn hiện tại sang đoạn mới.
  • Kiểu của các ký tự được chèn được xác định tự động, thường giữ cùng một kiểu văn bản đã tồn tại ở chỉ mục chèn. Do đó, văn bản thường được chèn vào TextRun hiện có tại chỉ mục đó. Bạn có thể cập nhật kiểu này sau bằng cách sử dụng yêu cầu UpdateTextStyle.

Đang xoá văn bản

Bạn có thể xoá một dải văn bản bằng cách sử dụng thông báo DeleteTextRequest trong lệnh gọi đến batchUpdate. Việc xoá văn bản có một số vấn đề tinh vi:

  • Thao tác xoá vượt quá một ranh giới của đoạn sẽ hợp nhất hai đoạn văn bản bằng cách xoá thành phần văn bản ParagraphMarker phân tách.
  • Đoạn mới được hợp nhất sẽ sử dụng kiểu đoạn kết hợp, khớp với hành vi trong trình chỉnh sửa Trang trình bày.
  • Thao tác xoá có phạm vi bao gồm một lần chạy văn bản sẽ xoá toàn bộ nội dung khỏi một lần chạy văn bản, đồng thời cũng sẽ xoá chính lần chạy văn bản đó.
  • Thao tác xoá có phạm vi bao gồm một phần tử AutoText sẽ xoá phần tử AutoText.

Đang cập nhật kiểu văn bản

Giao diện kết xuất của văn bản trong một trang trình bày được xác định bởi các thuộc tính kiểu văn bản:

  • Các kiểu đoạn như thụt lề, căn chỉnh và dấu đầu dòng được xác định theo các thuộc tính trên điểm đánh dấu đoạn.
  • Các kiểu ký tự như in đậm, in nghiêng và gạch dưới được xác định theo các thuộc tính khi chạy văn bản riêng lẻ.

Đang cập nhật kiểu nhân vật

Bạn có thể cập nhật kiểu ký tự bằng cách sử dụng thông báo UpdateTextStyleRequest trong lệnh gọi đến gói batchUpdate.

Giống như các thao tác văn bản khác, kiểu ký tự được áp dụng cho một loạt văn bản và ngầm tạo đối tượng TextRun mới khi cần.

Việc đặt một số kiểu ký tự ngầm cập nhật các kiểu liên quan khác để phù hợp với hành vi trong Trình chỉnh sửa Trang trình bày. Ví dụ: việc thêm một đường liên kết sẽ tự động thay đổi các thuộc tính gạch chân và màu nền trước của văn bản. Hãy xem tài liệu tham khảo về TextStyle để biết thêm thông tin chi tiết.

Đang cập nhật kiểu đoạn

Bạn có thể cập nhật kiểu đoạn văn bản bằng cách sử dụng thông báo UpdateParagraphStyleRequest trong lệnh gọi tới batchUpdate.

API Trang trình bày hỗ trợ CreateParagraphBulletsRequest phản ánh chức năng của giá trị đặt trước cho dấu đầu dòng trong trình chỉnh sửa Trang trình bày để tạo danh sách có dấu đầu dòng và được đánh số. Tương tự, DeleteParagraphBulletsRequest xoá mọi dấu đầu dòng hiện có trên đoạn.

Kiểu kế thừa

Một số hình dạng, còn gọi là placeholders, có thể kế thừa kiểu văn bản của các hình dạng mẹ khác: xem placeholders để tìm hiểu thêm về tính kế thừa của hình dạng nói chung.

Phần này tập trung vào cách hoạt động của tính kế thừa kiểu để tạo kiểu văn bản được kết xuất cuối cùng hiển thị trong trang trình bày.

Biểu thị kiểu trong phần giữ chỗ

Phần trong placeholders mô tả cách tính kế thừa hoạt động giữa hình dạng mẹ và hình dạng con. Việc kế thừa kiểu văn bản được xử lý bằng các tính năng bổ sung trong mô hình kế thừa:

  • Thuộc tính của các phần tử văn bản ParagraphMaker xác định định dạng đoạn.
  • Thuộc tính của các phần tử văn bản TextRun xác định định dạng ký tự.
  • Nội dung của phần giữ chỗ mẹ chứa 8 cặp ParagraphMarker/TextRun như vậy (để hỗ trợ 8 cấp độ lồng danh sách).
  • Phần giữ chỗ con kế thừa các thuộc tính văn bản mặc định từ các phần tử văn bản này trong nội dung văn bản của phần tử mẹ.

Biểu đồ sau đây cho thấy một cách trực quan hoá các mối quan hệ này:

sơ đồ hình dạng con kế thừa các thuộc tính văn bản

ParagraphMarker/TextRun đầu tiên trong hình dạng mẹ xác định hầu hết kiểu văn bản kế thừa; kiểu trong 7 cặp còn lại chỉ ảnh hưởng đến các đoạn ở cấp độ dấu đầu dòng lồng nhau sâu hơn:

Cặp thành phần văn bản gốc Định dạng con mà chế độ này kiểm soát
ParagraphMarker
TextRun đầu tiên
Kiểu văn bản của các đoạn danh sách cấp 0 (ngoài cùng) và tất cả các đoạn không nằm trong danh sách.
2 ParagraphMarker
thứ 2 TextRun
Kiểu văn bản của các cấp danh sách còn lại (lồng nhau) từ 1 đến 7
Thứ ba ParagraphMarker
Thứ ba TextRun
Thứ tư ParagraphMarker
Thứ tư TextRun
Thứ năm ParagraphMarker
thứ năm TextRun
Thứ sáu ParagraphMarker
Thứ sáu TextRun
Thứ bảy ParagraphMarker
Thứ bảy TextRun
Phần 8 ParagraphMarker
Phần 8 TextRun

Để truy cập các cặp thành phần văn bản này, hãy sử dụng chỉ mục rõ ràng trong trường textElements như minh hoạ trong đoạn mã bên dưới. Đoạn mã này cho biết cách thiết lập kiểu mặc định (kế thừa) cho cấp 0 và đoạn không phải trong danh sách:

"text": {
  "textElements": [  {
     "startIndex": 0,
     "endIndex": 1,
     "paragraphMarker": {
       "style": {  "alignment": "START",  ...  },
       "bullet": {  "nestingLevel": 0,  ...  }
     }
   },{
     "startIndex": 0,
     "endIndex": 1,
     "textRun": {
       "content": "\n",
       "style": {  "foregroundColor": {  "opaqueColor": {  "themeColor": "DARK1"  }  },  }
     }
   },{
     ...
   } ]
 }

Lưu ý rằng trường content của TextRun của hình dạng mẹ luôn bao gồm một ký tự dòng mới.

Kiểu kế thừa có thể được ghi đè

Hình dạng con có thể chỉ định các thuộc tính định kiểu trên các phần tử ParagraphMarkerTextRun trong nội dung. Các thuộc tính được chỉ định cục bộ này sẽ ghi đè mọi thuộc tính kế thừa trong phạm vi cục bộ của chúng. Các phần tử không chỉ định kiểu nào sẽ sử dụng kiểu tương ứng kế thừa từ kiểu gốc.

Việc xoá thuộc tính kiểu rõ ràng khỏi một hình dạng con để thuộc tính này không còn được đặt nữa, sẽ làm cho thuộc tính này kế thừa từ hình dạng mẹ.

Ví dụ:

Theo tính kế thừa hiển thị trong sơ đồ trên, giả sử hình dạng ParentPlaceholder có nội dung văn bản sau:

"text": {
  "textElements": [
    { "startIndex": 0,  "endIndex": 1,
      "paragraphMarker": {
        "style": {"alignment": "START", ...},
        "bullet": {"nestingLevel": 0, ...}
      }
    },
    { "startIndex": 0,  "endIndex": 1,
      "textRun": {
        "content": "\n",
        "style": {"foregroundColor": {"opaqueColor": {"themeColor": "DARK1"} }, }
        ...
      }
    },
    { "startIndex": 1,  "endIndex": 2,
      "paragraphMarker": {
        "style": {"alignment": "END", ...},
        "bullet": {"nestingLevel": 1, ...}
      }
    },
    { "startIndex": 1,  "endIndex": 2,
      "textRun": {
        "content": "\n",
        "style": {"foregroundColor": {"opaqueColor": {"themeColor": "LIGHT1"} }, ...}
      }
    },
   ...
  ]
}

Và giả sử hình dạng ChildPlaceholder có nội dung văn bản sau:

"text": {
  "textElements": [
    { "startIndex": 0,  "endIndex": 1,
      "paragraphMarker": {
        "style": {},
      }
    },
    { "startIndex": 0,  "endIndex": 1,
      "textRun": {
        "content": "This is my first paragraph\n",
        "style": {},
      }
      ...
    },
    {  "startIndex": 1,  "endIndex": 2,
      "paragraphMarker": {
        "style": {},
        "bullet": {
          "nestingLevel": 1,
          "listId": "someListId",
          "glyph": "●"
        }
      }
    },
    { "startIndex": 1,  "endIndex": 2,
      "textRun": {
        "content": "This paragraph is in a list\n",
        "style": {},
        ...
      }
    }
  ]
}

Điều này dẫn đến những kết quả được mô tả trong các đoạn sau.

Kế thừa kiểu cho một đoạn văn bản thuần tuý

Đoạn đầu tiên của hình dạng con, bao gồm văn bản "Đây là đoạn đầu tiên của tôi", là một đoạn văn đơn giản (không nằm trong danh sách). Không có phần tử nào trong nội dung văn bản chỉ định bất kỳ thuộc tính kiểu nào, vì vậy, thành phần này kế thừa tất cả các kiểu ký tự và đoạn văn bản từ thành phần mẹ. Điều này dẫn đến việc hiển thị sau:

  • Văn bản: "Đây là đoạn đầu tiên của tôi" là văn bản được hiển thị. Bản thân văn bản không bao giờ được kế thừa.
  • Căn chỉnh: Văn bản được hiển thị với cách căn chỉnh START, kế thừa từ ParagraphMarker đầu tiên của thành phần mẹ.
  • Màu nền trước: Văn bản được hiển thị bằng màu nền trước DARK1, kế thừa từ TextRun đầu tiên của thành phần mẹ.

Kế thừa kiểu của một đoạn trong danh sách

Đoạn tiếp theo, bao gồm cả văn bản "Đoạn này nằm trong danh sách", nằm trong danh sách có dấu đầu dòng ở cấp độ lồng ghép 1, vì ParagraphMarker tương ứng của đoạn văn bản đó có trường bullet được đặt ở cấp này. Do đó, lớp này sẽ kế thừa kiểu văn bản và đoạn văn bản từ cấp độ lồng nhau 1 trong thành phần mẹ. Điều này dẫn đến quá trình hiển thị như sau:

  • Văn bản: "Đoạn văn bản này nằm trong danh sách" là văn bản được hiển thị. Bản thân văn bản không bao giờ được kế thừa.
  • Căn chỉnh: Văn bản hiển thị với cách căn chỉnh "END", kế thừa từ ParagraphMarker thứ hai của thành phần mẹ.
  • Màu nền trước: Văn bản được hiển thị bằng màu nền trước của văn bản LIGHT1, kế thừa từ TextRun thứ hai của thành phần mẹ.

Tương tác giữa việc cập nhật và kế thừa kiểu văn bản và đoạn văn

Kiểu văn bản không được đặt theo hình dạng con sẽ kế thừa các giá trị từ thành phần mẹ. Kiểu văn bản được đặt trong thành phần con sẽ "ghi đè" các giá trị mẹ trong một số phạm vi cục bộ.

Bạn có thể sử dụng UpdateTextStyleRequest để huỷ thiết lập kiểu văn bản của hình dạng con để hình dạng đó không còn có chế độ ghi đè cục bộ nữa và do đó kế thừa các dấu gạch đứng từ hình dạng mẹ. Ngoài ra, việc cập nhật kiểu văn bản của thành phần con để khớp với giá trị kế thừa từ thành phần mẹ sẽ ngầm thiết lập kiểu đó để sử dụng giá trị kế thừa.

Điều này không ảnh hưởng đến giao diện trực quan của văn bản ngay sau khi cập nhật. Tuy nhiên, nếu sau đó bạn cập nhật một đoạn hoặc kiểu văn bản trong phần giữ chỗ gốc thì có thể sẽ quan trọng. Hành vi kế thừa này khớp với hành vi của trình chỉnh sửa Trang trình bày, vì vậy, bạn có thể thử nghiệm kết quả của việc thay đổi kiểu trước khi làm việc với API.

Ví dụ:

Hãy xem xét các định nghĩa trong ví dụ trước cho ChildPlaceholderParentPlaceholder.

Bây giờ, giả sử bạn gửi UpdateTextStyleRequest sau:

{ "updateTextStyle": {
    "objectId": "ChildPlaceholder",
    "style": {"foregroundColor": {"opaqueColor": {"themeColor": "DARK1"} }, },
    "textRange": { "type": "ALL" },
    "fields": "foregroundColor"
  }
}

Yêu cầu này cố gắng đặt DARK1foregroundColor cho tất cả văn bản của phần giữ chỗ cho trẻ em bằng cách sử dụng mặt nạ trường để chỉ định rằng chỉ màu nền trước của phần tử mới thay đổi. Yêu cầu này có các kết quả sau:

  • Đoạn đầu tiên: foregroundColor mới khớp với foregroundColor kế thừa, vì vậy kiểu này không thay đổi và vẫn kế thừa.
  • Đoạn thứ hai: foregroundColor mới không khớp với foregroundColor kế thừa, vì vậy màu nền trước của đoạn thứ hai được cập nhật thành DARK1.

Nội dung văn bản của Trình giữ chỗ con hiện là:

"text": {
  "textElements": [
    { "startIndex": 0,  "endIndex": 1,
      "paragraphMarker": {
        "style": {},
      }
    },
    { "startIndex": 0,  "endIndex": 1,
      "textRun": {
        "content": "This is my first paragraph\n",
        "style": {},
      }
      ...
    },
    { "startIndex": 1,  "endIndex": 2,
      "paragraphMarker": {
        "style": {},
        "bullet": {"nestingLevel": 1, "listId": "someListId", "glyph": "●" }
      }
    },
    { "startIndex": 1,  "endIndex": 2,
      "textRun": {
        "content": "This paragraph is in a list\n",
        "style": {"foregroundColor": {"opaqueColor": {"themeColor": "DARK1"} }, },
        ...
      }
    }
  ]
}

Kiểu văn bản dạng ký tự trong dấu đầu dòng

Giống như văn bản thông thường, ký tự gạch đầu dòng có kiểu văn bản kiểm soát cách hiển thị ký tự. Bạn không thể trực tiếp sửa đổi các kiểu văn bản này bằng API Trang trình bày. Tuy nhiên, nếu bạn sử dụng UpdateTextStyleRequest để cập nhật một đoạn hoàn chỉnh có dấu đầu dòng, thì API Trang trình bày sẽ cập nhật kiểu văn bản của dấu đầu dòng cho phù hợp.

Kiểu văn bản dùng dấu đầu dòng tuân theo hệ thống phân cấp kế thừa hơi khác so với kiểu văn bản thông thường.

  1. Dấu đầu dòng ở cấp độ lồng nhất nhất định kế thừa từ TextStyle được đặt trong trường NestingLevel.bullet_style bên trong đối tượng List của dấu đầu dòng.
  2. Tiếp theo, thành phần này kế thừa từ NestingLevel.bullet_style tương ứng trong List của phần giữ chỗ gốc.
  3. Cuối cùng, lớp này tìm cách kế thừa từ các đối tượng phần giữ chỗ mẹ còn lại.