add rich output support for slide notes
Docker server image / build-and-push (push) Successful in 3m2s

This commit is contained in:
2026-05-07 10:35:37 -07:00
parent 500b767d58
commit 06d4122e4e
11 changed files with 831 additions and 140 deletions
+42
View File
@@ -21,10 +21,28 @@ type RasterTierOptions struct {
JPEGQuality int32
}
// HtmlFormattingPolicy configures which formatting features are ignored in HTML notes mode.
type HtmlFormattingPolicy struct {
IgnoreBold bool
IgnoreItalic bool
IgnoreUnderline bool
IgnoreStrikethrough bool
IgnoreFontSize bool
IgnoreColor bool
}
// NotesOptions controls speaker-notes extraction/output.
type NotesOptions struct {
Format officeconvertapiv1.NotesFormat
HTMLUseParagraphTags *bool
HTMLPolicy *HtmlFormattingPolicy
}
// CreateConversionOptions configures optional full and thumbnail raster tiers.
type CreateConversionOptions struct {
Full *RasterTierOptions
Thumbnail *RasterTierOptions
Notes *NotesOptions
}
// Client wraps the generated Connect client with orchestration-focused helpers.
@@ -67,6 +85,7 @@ func (c *Client) CreateConversion(
if options != nil {
message.Full = toProtoSlideRasterOptions(options.Full)
message.Thumbnail = toProtoSlideRasterOptions(options.Thumbnail)
message.Notes = toProtoNotesOptions(options.Notes)
}
req := connect.NewRequest(message)
res, err := c.rpc.CreateConversion(ctx, req)
@@ -95,6 +114,29 @@ func toProtoSlideRasterOptions(
return proto
}
func toProtoNotesOptions(options *NotesOptions) *officeconvertapiv1.NotesOptions {
if options == nil {
return nil
}
proto := &officeconvertapiv1.NotesOptions{
Format: options.Format,
}
if options.HTMLUseParagraphTags != nil {
proto.HtmlUseParagraphTags = options.HTMLUseParagraphTags
}
if options.HTMLPolicy != nil {
proto.HtmlPolicy = &officeconvertapiv1.HtmlFormattingPolicy{
IgnoreBold: options.HTMLPolicy.IgnoreBold,
IgnoreItalic: options.HTMLPolicy.IgnoreItalic,
IgnoreUnderline: options.HTMLPolicy.IgnoreUnderline,
IgnoreStrikethrough: options.HTMLPolicy.IgnoreStrikethrough,
IgnoreFontSize: options.HTMLPolicy.IgnoreFontSize,
IgnoreColor: options.HTMLPolicy.IgnoreColor,
}
}
return proto
}
// StartConversion signals that upload is complete and conversion can begin.
func (c *Client) StartConversion(
ctx context.Context,
+332 -88
View File
@@ -196,6 +196,56 @@ func (ConversionResolution) EnumDescriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{2}
}
// NotesFormat controls what note representation the server should compute.
type NotesFormat int32
const (
NotesFormat_NOTES_FORMAT_UNSPECIFIED NotesFormat = 0
NotesFormat_NOTES_FORMAT_PLAIN NotesFormat = 1
NotesFormat_NOTES_FORMAT_HTML NotesFormat = 2
)
// Enum value maps for NotesFormat.
var (
NotesFormat_name = map[int32]string{
0: "NOTES_FORMAT_UNSPECIFIED",
1: "NOTES_FORMAT_PLAIN",
2: "NOTES_FORMAT_HTML",
}
NotesFormat_value = map[string]int32{
"NOTES_FORMAT_UNSPECIFIED": 0,
"NOTES_FORMAT_PLAIN": 1,
"NOTES_FORMAT_HTML": 2,
}
)
func (x NotesFormat) Enum() *NotesFormat {
p := new(NotesFormat)
*p = x
return p
}
func (x NotesFormat) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (NotesFormat) Descriptor() protoreflect.EnumDescriptor {
return file_officeconvertapi_v1_conversion_proto_enumTypes[3].Descriptor()
}
func (NotesFormat) Type() protoreflect.EnumType {
return &file_officeconvertapi_v1_conversion_proto_enumTypes[3]
}
func (x NotesFormat) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use NotesFormat.Descriptor instead.
func (NotesFormat) EnumDescriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{3}
}
// JpegOutputOptions configures JPEG-specific encoding controls.
type JpegOutputOptions struct {
state protoimpl.MessageState `protogen:"open.v1"`
@@ -318,6 +368,155 @@ type SlideRasterOptions_Jpeg struct {
func (*SlideRasterOptions_Jpeg) isSlideRasterOptions_Format() {}
// HtmlFormattingPolicy configures which formatting features are ignored in HTML mode.
type HtmlFormattingPolicy struct {
state protoimpl.MessageState `protogen:"open.v1"`
IgnoreBold bool `protobuf:"varint,1,opt,name=ignore_bold,json=ignoreBold,proto3" json:"ignore_bold,omitempty"`
IgnoreItalic bool `protobuf:"varint,2,opt,name=ignore_italic,json=ignoreItalic,proto3" json:"ignore_italic,omitempty"`
IgnoreUnderline bool `protobuf:"varint,3,opt,name=ignore_underline,json=ignoreUnderline,proto3" json:"ignore_underline,omitempty"`
IgnoreStrikethrough bool `protobuf:"varint,4,opt,name=ignore_strikethrough,json=ignoreStrikethrough,proto3" json:"ignore_strikethrough,omitempty"`
IgnoreFontSize bool `protobuf:"varint,5,opt,name=ignore_font_size,json=ignoreFontSize,proto3" json:"ignore_font_size,omitempty"`
IgnoreColor bool `protobuf:"varint,6,opt,name=ignore_color,json=ignoreColor,proto3" json:"ignore_color,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HtmlFormattingPolicy) Reset() {
*x = HtmlFormattingPolicy{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *HtmlFormattingPolicy) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HtmlFormattingPolicy) ProtoMessage() {}
func (x *HtmlFormattingPolicy) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HtmlFormattingPolicy.ProtoReflect.Descriptor instead.
func (*HtmlFormattingPolicy) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{2}
}
func (x *HtmlFormattingPolicy) GetIgnoreBold() bool {
if x != nil {
return x.IgnoreBold
}
return false
}
func (x *HtmlFormattingPolicy) GetIgnoreItalic() bool {
if x != nil {
return x.IgnoreItalic
}
return false
}
func (x *HtmlFormattingPolicy) GetIgnoreUnderline() bool {
if x != nil {
return x.IgnoreUnderline
}
return false
}
func (x *HtmlFormattingPolicy) GetIgnoreStrikethrough() bool {
if x != nil {
return x.IgnoreStrikethrough
}
return false
}
func (x *HtmlFormattingPolicy) GetIgnoreFontSize() bool {
if x != nil {
return x.IgnoreFontSize
}
return false
}
func (x *HtmlFormattingPolicy) GetIgnoreColor() bool {
if x != nil {
return x.IgnoreColor
}
return false
}
// NotesOptions configures note extraction behavior.
type NotesOptions struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Output format for extracted notes.
Format NotesFormat `protobuf:"varint,1,opt,name=format,proto3,enum=officeconvertapi.v1.NotesFormat" json:"format,omitempty"`
// If true, wrap PPTX paragraphs as <p> blocks in HTML mode. If false, emit <br/> only.
HtmlUseParagraphTags *bool `protobuf:"varint,2,opt,name=html_use_paragraph_tags,json=htmlUseParagraphTags,proto3,oneof" json:"html_use_paragraph_tags,omitempty"`
// Formatting policy for HTML mode.
HtmlPolicy *HtmlFormattingPolicy `protobuf:"bytes,3,opt,name=html_policy,json=htmlPolicy,proto3" json:"html_policy,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *NotesOptions) Reset() {
*x = NotesOptions{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *NotesOptions) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NotesOptions) ProtoMessage() {}
func (x *NotesOptions) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NotesOptions.ProtoReflect.Descriptor instead.
func (*NotesOptions) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{3}
}
func (x *NotesOptions) GetFormat() NotesFormat {
if x != nil {
return x.Format
}
return NotesFormat_NOTES_FORMAT_UNSPECIFIED
}
func (x *NotesOptions) GetHtmlUseParagraphTags() bool {
if x != nil && x.HtmlUseParagraphTags != nil {
return *x.HtmlUseParagraphTags
}
return false
}
func (x *NotesOptions) GetHtmlPolicy() *HtmlFormattingPolicy {
if x != nil {
return x.HtmlPolicy
}
return nil
}
// Slide contains extracted notes and the rendered image URL for one slide.
type Slide struct {
state protoimpl.MessageState `protogen:"open.v1"`
@@ -327,13 +526,15 @@ type Slide struct {
ImageUrl string `protobuf:"bytes,3,opt,name=image_url,json=imageUrl,proto3" json:"image_url,omitempty"`
// Thumbnail rendered image URL.
ThumbnailImageUrl string `protobuf:"bytes,4,opt,name=thumbnail_image_url,json=thumbnailImageUrl,proto3" json:"thumbnail_image_url,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
// Optional, sanitized HTML version of notes when requested.
NotesHtml string `protobuf:"bytes,5,opt,name=notes_html,json=notesHtml,proto3" json:"notes_html,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Slide) Reset() {
*x = Slide{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[2]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -345,7 +546,7 @@ func (x *Slide) String() string {
func (*Slide) ProtoMessage() {}
func (x *Slide) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[2]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -358,7 +559,7 @@ func (x *Slide) ProtoReflect() protoreflect.Message {
// Deprecated: Use Slide.ProtoReflect.Descriptor instead.
func (*Slide) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{2}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{4}
}
func (x *Slide) GetIndex() int32 {
@@ -389,6 +590,13 @@ func (x *Slide) GetThumbnailImageUrl() string {
return ""
}
func (x *Slide) GetNotesHtml() string {
if x != nil {
return x.NotesHtml
}
return ""
}
// SlideDeck is the final structured conversion artifact.
type SlideDeck struct {
state protoimpl.MessageState `protogen:"open.v1"`
@@ -411,7 +619,7 @@ type SlideDeck struct {
func (x *SlideDeck) Reset() {
*x = SlideDeck{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[3]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -423,7 +631,7 @@ func (x *SlideDeck) String() string {
func (*SlideDeck) ProtoMessage() {}
func (x *SlideDeck) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[3]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -436,7 +644,7 @@ func (x *SlideDeck) ProtoReflect() protoreflect.Message {
// Deprecated: Use SlideDeck.ProtoReflect.Descriptor instead.
func (*SlideDeck) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{3}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{5}
}
func (x *SlideDeck) GetConversionId() string {
@@ -501,13 +709,14 @@ type CreateConversionRequest struct {
SourceFilename string `protobuf:"bytes,1,opt,name=source_filename,json=sourceFilename,proto3" json:"source_filename,omitempty"`
Full *SlideRasterOptions `protobuf:"bytes,2,opt,name=full,proto3" json:"full,omitempty"`
Thumbnail *SlideRasterOptions `protobuf:"bytes,3,opt,name=thumbnail,proto3" json:"thumbnail,omitempty"`
Notes *NotesOptions `protobuf:"bytes,4,opt,name=notes,proto3" json:"notes,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreateConversionRequest) Reset() {
*x = CreateConversionRequest{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[4]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -519,7 +728,7 @@ func (x *CreateConversionRequest) String() string {
func (*CreateConversionRequest) ProtoMessage() {}
func (x *CreateConversionRequest) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[4]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -532,7 +741,7 @@ func (x *CreateConversionRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreateConversionRequest.ProtoReflect.Descriptor instead.
func (*CreateConversionRequest) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{4}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{6}
}
func (x *CreateConversionRequest) GetSourceFilename() string {
@@ -556,6 +765,13 @@ func (x *CreateConversionRequest) GetThumbnail() *SlideRasterOptions {
return nil
}
func (x *CreateConversionRequest) GetNotes() *NotesOptions {
if x != nil {
return x.Notes
}
return nil
}
// CreateConversionResponse returns upload details for the session.
type CreateConversionResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
@@ -571,7 +787,7 @@ type CreateConversionResponse struct {
func (x *CreateConversionResponse) Reset() {
*x = CreateConversionResponse{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[5]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -583,7 +799,7 @@ func (x *CreateConversionResponse) String() string {
func (*CreateConversionResponse) ProtoMessage() {}
func (x *CreateConversionResponse) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[5]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -596,7 +812,7 @@ func (x *CreateConversionResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreateConversionResponse.ProtoReflect.Descriptor instead.
func (*CreateConversionResponse) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{5}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{7}
}
func (x *CreateConversionResponse) GetConversionId() string {
@@ -645,7 +861,7 @@ type StartConversionRequest struct {
func (x *StartConversionRequest) Reset() {
*x = StartConversionRequest{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[6]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -657,7 +873,7 @@ func (x *StartConversionRequest) String() string {
func (*StartConversionRequest) ProtoMessage() {}
func (x *StartConversionRequest) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[6]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -670,7 +886,7 @@ func (x *StartConversionRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use StartConversionRequest.ProtoReflect.Descriptor instead.
func (*StartConversionRequest) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{6}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{8}
}
func (x *StartConversionRequest) GetConversionId() string {
@@ -692,7 +908,7 @@ type StartConversionResponse struct {
func (x *StartConversionResponse) Reset() {
*x = StartConversionResponse{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[7]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -704,7 +920,7 @@ func (x *StartConversionResponse) String() string {
func (*StartConversionResponse) ProtoMessage() {}
func (x *StartConversionResponse) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[7]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -717,7 +933,7 @@ func (x *StartConversionResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use StartConversionResponse.ProtoReflect.Descriptor instead.
func (*StartConversionResponse) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{7}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{9}
}
func (x *StartConversionResponse) GetConversionId() string {
@@ -745,7 +961,7 @@ type GetConversionStatusRequest struct {
func (x *GetConversionStatusRequest) Reset() {
*x = GetConversionStatusRequest{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[8]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -757,7 +973,7 @@ func (x *GetConversionStatusRequest) String() string {
func (*GetConversionStatusRequest) ProtoMessage() {}
func (x *GetConversionStatusRequest) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[8]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -770,7 +986,7 @@ func (x *GetConversionStatusRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetConversionStatusRequest.ProtoReflect.Descriptor instead.
func (*GetConversionStatusRequest) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{8}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{10}
}
func (x *GetConversionStatusRequest) GetConversionId() string {
@@ -797,7 +1013,7 @@ type GetConversionStatusResponse struct {
func (x *GetConversionStatusResponse) Reset() {
*x = GetConversionStatusResponse{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[9]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -809,7 +1025,7 @@ func (x *GetConversionStatusResponse) String() string {
func (*GetConversionStatusResponse) ProtoMessage() {}
func (x *GetConversionStatusResponse) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[9]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -822,7 +1038,7 @@ func (x *GetConversionStatusResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetConversionStatusResponse.ProtoReflect.Descriptor instead.
func (*GetConversionStatusResponse) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{9}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{11}
}
func (x *GetConversionStatusResponse) GetConversionId() string {
@@ -885,7 +1101,7 @@ type GetSlideDeckRequest struct {
func (x *GetSlideDeckRequest) Reset() {
*x = GetSlideDeckRequest{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[10]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -897,7 +1113,7 @@ func (x *GetSlideDeckRequest) String() string {
func (*GetSlideDeckRequest) ProtoMessage() {}
func (x *GetSlideDeckRequest) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[10]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[12]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -910,7 +1126,7 @@ func (x *GetSlideDeckRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetSlideDeckRequest.ProtoReflect.Descriptor instead.
func (*GetSlideDeckRequest) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{10}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{12}
}
func (x *GetSlideDeckRequest) GetConversionId() string {
@@ -930,7 +1146,7 @@ type GetSlideDeckResponse struct {
func (x *GetSlideDeckResponse) Reset() {
*x = GetSlideDeckResponse{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[11]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -942,7 +1158,7 @@ func (x *GetSlideDeckResponse) String() string {
func (*GetSlideDeckResponse) ProtoMessage() {}
func (x *GetSlideDeckResponse) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[11]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[13]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -955,7 +1171,7 @@ func (x *GetSlideDeckResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetSlideDeckResponse.ProtoReflect.Descriptor instead.
func (*GetSlideDeckResponse) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{11}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{13}
}
func (x *GetSlideDeckResponse) GetSlideDeck() *SlideDeck {
@@ -976,7 +1192,7 @@ type DeleteConversionRequest struct {
func (x *DeleteConversionRequest) Reset() {
*x = DeleteConversionRequest{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[12]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -988,7 +1204,7 @@ func (x *DeleteConversionRequest) String() string {
func (*DeleteConversionRequest) ProtoMessage() {}
func (x *DeleteConversionRequest) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[12]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[14]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1001,7 +1217,7 @@ func (x *DeleteConversionRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeleteConversionRequest.ProtoReflect.Descriptor instead.
func (*DeleteConversionRequest) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{12}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{14}
}
func (x *DeleteConversionRequest) GetConversionId() string {
@@ -1023,7 +1239,7 @@ type DeleteConversionResponse struct {
func (x *DeleteConversionResponse) Reset() {
*x = DeleteConversionResponse{}
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[13]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1035,7 +1251,7 @@ func (x *DeleteConversionResponse) String() string {
func (*DeleteConversionResponse) ProtoMessage() {}
func (x *DeleteConversionResponse) ProtoReflect() protoreflect.Message {
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[13]
mi := &file_officeconvertapi_v1_conversion_proto_msgTypes[15]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1048,7 +1264,7 @@ func (x *DeleteConversionResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeleteConversionResponse.ProtoReflect.Descriptor instead.
func (*DeleteConversionResponse) Descriptor() ([]byte, []int) {
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{13}
return file_officeconvertapi_v1_conversion_proto_rawDescGZIP(), []int{15}
}
func (x *DeleteConversionResponse) GetConversionId() string {
@@ -1077,13 +1293,29 @@ const file_officeconvertapi_v1_conversion_proto_rawDesc = "" +
"resolution\x18\x01 \x01(\x0e2).officeconvertapi.v1.ConversionResolutionR\n" +
"resolution\x12<\n" +
"\x04jpeg\x18\x02 \x01(\v2&.officeconvertapi.v1.JpegOutputOptionsH\x00R\x04jpegB\b\n" +
"\x06format\"\x8b\x01\n" +
"\x06format\"\x87\x02\n" +
"\x14HtmlFormattingPolicy\x12\x1f\n" +
"\vignore_bold\x18\x01 \x01(\bR\n" +
"ignoreBold\x12#\n" +
"\rignore_italic\x18\x02 \x01(\bR\fignoreItalic\x12)\n" +
"\x10ignore_underline\x18\x03 \x01(\bR\x0fignoreUnderline\x121\n" +
"\x14ignore_strikethrough\x18\x04 \x01(\bR\x13ignoreStrikethrough\x12(\n" +
"\x10ignore_font_size\x18\x05 \x01(\bR\x0eignoreFontSize\x12!\n" +
"\fignore_color\x18\x06 \x01(\bR\vignoreColor\"\xec\x01\n" +
"\fNotesOptions\x128\n" +
"\x06format\x18\x01 \x01(\x0e2 .officeconvertapi.v1.NotesFormatR\x06format\x12:\n" +
"\x17html_use_paragraph_tags\x18\x02 \x01(\bH\x00R\x14htmlUseParagraphTags\x88\x01\x01\x12J\n" +
"\vhtml_policy\x18\x03 \x01(\v2).officeconvertapi.v1.HtmlFormattingPolicyR\n" +
"htmlPolicyB\x1a\n" +
"\x18_html_use_paragraph_tags\"\xaa\x01\n" +
"\x05Slide\x12\x14\n" +
"\x05index\x18\x01 \x01(\x05R\x05index\x12\x1f\n" +
"\vnotes_plain\x18\x02 \x01(\tR\n" +
"notesPlain\x12\x1b\n" +
"\timage_url\x18\x03 \x01(\tR\bimageUrl\x12.\n" +
"\x13thumbnail_image_url\x18\x04 \x01(\tR\x11thumbnailImageUrl\"\xca\x02\n" +
"\x13thumbnail_image_url\x18\x04 \x01(\tR\x11thumbnailImageUrl\x12\x1d\n" +
"\n" +
"notes_html\x18\x05 \x01(\tR\tnotesHtml\"\xca\x02\n" +
"\tSlideDeck\x12#\n" +
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\x12'\n" +
"\x0fsource_filename\x18\x02 \x01(\tR\x0esourceFilename\x122\n" +
@@ -1093,11 +1325,12 @@ const file_officeconvertapi_v1_conversion_proto_rawDesc = "" +
"\x05width\x18\x05 \x01(\x05R\x05width\x12\x16\n" +
"\x06height\x18\x06 \x01(\x05R\x06height\x12'\n" +
"\x0fthumbnail_width\x18\a \x01(\x05R\x0ethumbnailWidth\x12)\n" +
"\x10thumbnail_height\x18\b \x01(\x05R\x0fthumbnailHeight\"\xc6\x01\n" +
"\x10thumbnail_height\x18\b \x01(\x05R\x0fthumbnailHeight\"\xff\x01\n" +
"\x17CreateConversionRequest\x12'\n" +
"\x0fsource_filename\x18\x01 \x01(\tR\x0esourceFilename\x12;\n" +
"\x04full\x18\x02 \x01(\v2'.officeconvertapi.v1.SlideRasterOptionsR\x04full\x12E\n" +
"\tthumbnail\x18\x03 \x01(\v2'.officeconvertapi.v1.SlideRasterOptionsR\tthumbnail\"\xea\x01\n" +
"\tthumbnail\x18\x03 \x01(\v2'.officeconvertapi.v1.SlideRasterOptionsR\tthumbnail\x127\n" +
"\x05notes\x18\x04 \x01(\v2!.officeconvertapi.v1.NotesOptionsR\x05notes\"\xea\x01\n" +
"\x18CreateConversionResponse\x12#\n" +
"\rconversion_id\x18\x01 \x01(\tR\fconversionId\x12#\n" +
"\rupload_bucket\x18\x02 \x01(\tR\fuploadBucket\x12*\n" +
@@ -1151,7 +1384,11 @@ const file_officeconvertapi_v1_conversion_proto_rawDesc = "" +
"\x18CONVERSION_RESOLUTION_HD\x10\x02\x12\x1d\n" +
"\x19CONVERSION_RESOLUTION_FHD\x10\x03\x12\x1d\n" +
"\x19CONVERSION_RESOLUTION_QHD\x10\x04\x12\x1d\n" +
"\x19CONVERSION_RESOLUTION_UHD\x10\x052\xcc\x04\n" +
"\x19CONVERSION_RESOLUTION_UHD\x10\x05*Z\n" +
"\vNotesFormat\x12\x1c\n" +
"\x18NOTES_FORMAT_UNSPECIFIED\x10\x00\x12\x16\n" +
"\x12NOTES_FORMAT_PLAIN\x10\x01\x12\x15\n" +
"\x11NOTES_FORMAT_HTML\x10\x022\xcc\x04\n" +
"\x11ConversionService\x12q\n" +
"\x10CreateConversion\x12,.officeconvertapi.v1.CreateConversionRequest\x1a-.officeconvertapi.v1.CreateConversionResponse\"\x00\x12n\n" +
"\x0fStartConversion\x12+.officeconvertapi.v1.StartConversionRequest\x1a,.officeconvertapi.v1.StartConversionResponse\"\x00\x12z\n" +
@@ -1171,56 +1408,62 @@ func file_officeconvertapi_v1_conversion_proto_rawDescGZIP() []byte {
return file_officeconvertapi_v1_conversion_proto_rawDescData
}
var file_officeconvertapi_v1_conversion_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
var file_officeconvertapi_v1_conversion_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
var file_officeconvertapi_v1_conversion_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
var file_officeconvertapi_v1_conversion_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
var file_officeconvertapi_v1_conversion_proto_goTypes = []any{
(ConversionStatus)(0), // 0: officeconvertapi.v1.ConversionStatus
(ConversionPhase)(0), // 1: officeconvertapi.v1.ConversionPhase
(ConversionResolution)(0), // 2: officeconvertapi.v1.ConversionResolution
(*JpegOutputOptions)(nil), // 3: officeconvertapi.v1.JpegOutputOptions
(*SlideRasterOptions)(nil), // 4: officeconvertapi.v1.SlideRasterOptions
(*Slide)(nil), // 5: officeconvertapi.v1.Slide
(*SlideDeck)(nil), // 6: officeconvertapi.v1.SlideDeck
(*CreateConversionRequest)(nil), // 7: officeconvertapi.v1.CreateConversionRequest
(*CreateConversionResponse)(nil), // 8: officeconvertapi.v1.CreateConversionResponse
(*StartConversionRequest)(nil), // 9: officeconvertapi.v1.StartConversionRequest
(*StartConversionResponse)(nil), // 10: officeconvertapi.v1.StartConversionResponse
(*GetConversionStatusRequest)(nil), // 11: officeconvertapi.v1.GetConversionStatusRequest
(*GetConversionStatusResponse)(nil), // 12: officeconvertapi.v1.GetConversionStatusResponse
(*GetSlideDeckRequest)(nil), // 13: officeconvertapi.v1.GetSlideDeckRequest
(*GetSlideDeckResponse)(nil), // 14: officeconvertapi.v1.GetSlideDeckResponse
(*DeleteConversionRequest)(nil), // 15: officeconvertapi.v1.DeleteConversionRequest
(*DeleteConversionResponse)(nil), // 16: officeconvertapi.v1.DeleteConversionResponse
(*timestamppb.Timestamp)(nil), // 17: google.protobuf.Timestamp
(NotesFormat)(0), // 3: officeconvertapi.v1.NotesFormat
(*JpegOutputOptions)(nil), // 4: officeconvertapi.v1.JpegOutputOptions
(*SlideRasterOptions)(nil), // 5: officeconvertapi.v1.SlideRasterOptions
(*HtmlFormattingPolicy)(nil), // 6: officeconvertapi.v1.HtmlFormattingPolicy
(*NotesOptions)(nil), // 7: officeconvertapi.v1.NotesOptions
(*Slide)(nil), // 8: officeconvertapi.v1.Slide
(*SlideDeck)(nil), // 9: officeconvertapi.v1.SlideDeck
(*CreateConversionRequest)(nil), // 10: officeconvertapi.v1.CreateConversionRequest
(*CreateConversionResponse)(nil), // 11: officeconvertapi.v1.CreateConversionResponse
(*StartConversionRequest)(nil), // 12: officeconvertapi.v1.StartConversionRequest
(*StartConversionResponse)(nil), // 13: officeconvertapi.v1.StartConversionResponse
(*GetConversionStatusRequest)(nil), // 14: officeconvertapi.v1.GetConversionStatusRequest
(*GetConversionStatusResponse)(nil), // 15: officeconvertapi.v1.GetConversionStatusResponse
(*GetSlideDeckRequest)(nil), // 16: officeconvertapi.v1.GetSlideDeckRequest
(*GetSlideDeckResponse)(nil), // 17: officeconvertapi.v1.GetSlideDeckResponse
(*DeleteConversionRequest)(nil), // 18: officeconvertapi.v1.DeleteConversionRequest
(*DeleteConversionResponse)(nil), // 19: officeconvertapi.v1.DeleteConversionResponse
(*timestamppb.Timestamp)(nil), // 20: google.protobuf.Timestamp
}
var file_officeconvertapi_v1_conversion_proto_depIdxs = []int32{
2, // 0: officeconvertapi.v1.SlideRasterOptions.resolution:type_name -> officeconvertapi.v1.ConversionResolution
3, // 1: officeconvertapi.v1.SlideRasterOptions.jpeg:type_name -> officeconvertapi.v1.JpegOutputOptions
5, // 2: officeconvertapi.v1.SlideDeck.slides:type_name -> officeconvertapi.v1.Slide
17, // 3: officeconvertapi.v1.SlideDeck.created_at:type_name -> google.protobuf.Timestamp
4, // 4: officeconvertapi.v1.CreateConversionRequest.full:type_name -> officeconvertapi.v1.SlideRasterOptions
4, // 5: officeconvertapi.v1.CreateConversionRequest.thumbnail:type_name -> officeconvertapi.v1.SlideRasterOptions
17, // 6: officeconvertapi.v1.CreateConversionResponse.expires_at:type_name -> google.protobuf.Timestamp
0, // 7: officeconvertapi.v1.StartConversionResponse.status:type_name -> officeconvertapi.v1.ConversionStatus
0, // 8: officeconvertapi.v1.GetConversionStatusResponse.status:type_name -> officeconvertapi.v1.ConversionStatus
17, // 9: officeconvertapi.v1.GetConversionStatusResponse.updated_at:type_name -> google.protobuf.Timestamp
1, // 10: officeconvertapi.v1.GetConversionStatusResponse.phase:type_name -> officeconvertapi.v1.ConversionPhase
6, // 11: officeconvertapi.v1.GetSlideDeckResponse.slide_deck:type_name -> officeconvertapi.v1.SlideDeck
7, // 12: officeconvertapi.v1.ConversionService.CreateConversion:input_type -> officeconvertapi.v1.CreateConversionRequest
9, // 13: officeconvertapi.v1.ConversionService.StartConversion:input_type -> officeconvertapi.v1.StartConversionRequest
11, // 14: officeconvertapi.v1.ConversionService.GetConversionStatus:input_type -> officeconvertapi.v1.GetConversionStatusRequest
13, // 15: officeconvertapi.v1.ConversionService.GetSlideDeck:input_type -> officeconvertapi.v1.GetSlideDeckRequest
15, // 16: officeconvertapi.v1.ConversionService.DeleteConversion:input_type -> officeconvertapi.v1.DeleteConversionRequest
8, // 17: officeconvertapi.v1.ConversionService.CreateConversion:output_type -> officeconvertapi.v1.CreateConversionResponse
10, // 18: officeconvertapi.v1.ConversionService.StartConversion:output_type -> officeconvertapi.v1.StartConversionResponse
12, // 19: officeconvertapi.v1.ConversionService.GetConversionStatus:output_type -> officeconvertapi.v1.GetConversionStatusResponse
14, // 20: officeconvertapi.v1.ConversionService.GetSlideDeck:output_type -> officeconvertapi.v1.GetSlideDeckResponse
16, // 21: officeconvertapi.v1.ConversionService.DeleteConversion:output_type -> officeconvertapi.v1.DeleteConversionResponse
17, // [17:22] is the sub-list for method output_type
12, // [12:17] is the sub-list for method input_type
12, // [12:12] is the sub-list for extension type_name
12, // [12:12] is the sub-list for extension extendee
0, // [0:12] is the sub-list for field type_name
4, // 1: officeconvertapi.v1.SlideRasterOptions.jpeg:type_name -> officeconvertapi.v1.JpegOutputOptions
3, // 2: officeconvertapi.v1.NotesOptions.format:type_name -> officeconvertapi.v1.NotesFormat
6, // 3: officeconvertapi.v1.NotesOptions.html_policy:type_name -> officeconvertapi.v1.HtmlFormattingPolicy
8, // 4: officeconvertapi.v1.SlideDeck.slides:type_name -> officeconvertapi.v1.Slide
20, // 5: officeconvertapi.v1.SlideDeck.created_at:type_name -> google.protobuf.Timestamp
5, // 6: officeconvertapi.v1.CreateConversionRequest.full:type_name -> officeconvertapi.v1.SlideRasterOptions
5, // 7: officeconvertapi.v1.CreateConversionRequest.thumbnail:type_name -> officeconvertapi.v1.SlideRasterOptions
7, // 8: officeconvertapi.v1.CreateConversionRequest.notes:type_name -> officeconvertapi.v1.NotesOptions
20, // 9: officeconvertapi.v1.CreateConversionResponse.expires_at:type_name -> google.protobuf.Timestamp
0, // 10: officeconvertapi.v1.StartConversionResponse.status:type_name -> officeconvertapi.v1.ConversionStatus
0, // 11: officeconvertapi.v1.GetConversionStatusResponse.status:type_name -> officeconvertapi.v1.ConversionStatus
20, // 12: officeconvertapi.v1.GetConversionStatusResponse.updated_at:type_name -> google.protobuf.Timestamp
1, // 13: officeconvertapi.v1.GetConversionStatusResponse.phase:type_name -> officeconvertapi.v1.ConversionPhase
9, // 14: officeconvertapi.v1.GetSlideDeckResponse.slide_deck:type_name -> officeconvertapi.v1.SlideDeck
10, // 15: officeconvertapi.v1.ConversionService.CreateConversion:input_type -> officeconvertapi.v1.CreateConversionRequest
12, // 16: officeconvertapi.v1.ConversionService.StartConversion:input_type -> officeconvertapi.v1.StartConversionRequest
14, // 17: officeconvertapi.v1.ConversionService.GetConversionStatus:input_type -> officeconvertapi.v1.GetConversionStatusRequest
16, // 18: officeconvertapi.v1.ConversionService.GetSlideDeck:input_type -> officeconvertapi.v1.GetSlideDeckRequest
18, // 19: officeconvertapi.v1.ConversionService.DeleteConversion:input_type -> officeconvertapi.v1.DeleteConversionRequest
11, // 20: officeconvertapi.v1.ConversionService.CreateConversion:output_type -> officeconvertapi.v1.CreateConversionResponse
13, // 21: officeconvertapi.v1.ConversionService.StartConversion:output_type -> officeconvertapi.v1.StartConversionResponse
15, // 22: officeconvertapi.v1.ConversionService.GetConversionStatus:output_type -> officeconvertapi.v1.GetConversionStatusResponse
17, // 23: officeconvertapi.v1.ConversionService.GetSlideDeck:output_type -> officeconvertapi.v1.GetSlideDeckResponse
19, // 24: officeconvertapi.v1.ConversionService.DeleteConversion:output_type -> officeconvertapi.v1.DeleteConversionResponse
20, // [20:25] is the sub-list for method output_type
15, // [15:20] is the sub-list for method input_type
15, // [15:15] is the sub-list for extension type_name
15, // [15:15] is the sub-list for extension extendee
0, // [0:15] is the sub-list for field type_name
}
func init() { file_officeconvertapi_v1_conversion_proto_init() }
@@ -1231,13 +1474,14 @@ func file_officeconvertapi_v1_conversion_proto_init() {
file_officeconvertapi_v1_conversion_proto_msgTypes[1].OneofWrappers = []any{
(*SlideRasterOptions_Jpeg)(nil),
}
file_officeconvertapi_v1_conversion_proto_msgTypes[3].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_officeconvertapi_v1_conversion_proto_rawDesc), len(file_officeconvertapi_v1_conversion_proto_rawDesc)),
NumEnums: 3,
NumMessages: 14,
NumEnums: 4,
NumMessages: 16,
NumExtensions: 0,
NumServices: 1,
},
@@ -7,6 +7,7 @@ from typing import Protocol
from connectrpc.client import ConnectClient, ConnectClientSync
from connectrpc.code import Code
from connectrpc.codec import Codec
from connectrpc.compression import Compression
from connectrpc.errors import ConnectError
from connectrpc.interceptor import Interceptor, InterceptorSync
@@ -34,7 +35,7 @@ class ConversionService(Protocol):
class ConversionServiceASGIApplication(ConnectASGIApplication[ConversionService]):
def __init__(self, service: ConversionService | AsyncGenerator[ConversionService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
def __init__(self, service: ConversionService | AsyncGenerator[ConversionService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None, codecs: Iterable[Codec] | None = None) -> None:
super().__init__(
service=service,
endpoints=lambda svc: {
@@ -92,6 +93,7 @@ class ConversionServiceASGIApplication(ConnectASGIApplication[ConversionService]
interceptors=interceptors,
read_max_bytes=read_max_bytes,
compressions=compressions,
codecs=codecs,
)
@property
@@ -202,6 +204,9 @@ class ConversionServiceClient(ConnectClient):
)
class ConversionServiceSync(Protocol):
def create_conversion(self, request: officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionRequest, ctx: RequestContext) -> officeconvertapi_dot_v1_dot_conversion__pb2.CreateConversionResponse:
raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
@@ -216,7 +221,7 @@ class ConversionServiceSync(Protocol):
class ConversionServiceWSGIApplication(ConnectWSGIApplication):
def __init__(self, service: ConversionServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
def __init__(self, service: ConversionServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None, codecs: Iterable[Codec] | None = None) -> None:
super().__init__(
endpoints={
"/officeconvertapi.v1.ConversionService/CreateConversion": EndpointSync.unary(
@@ -273,6 +278,7 @@ class ConversionServiceWSGIApplication(ConnectWSGIApplication):
interceptors=interceptors,
read_max_bytes=read_max_bytes,
compressions=compressions,
codecs=codecs,
)
@property
@@ -381,3 +387,5 @@ class ConversionServiceClientSync(ConnectClientSync):
headers=headers,
timeout_ms=timeout_ms,
)
File diff suppressed because one or more lines are too long
@@ -35,6 +35,12 @@ class ConversionResolution(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
CONVERSION_RESOLUTION_FHD: _ClassVar[ConversionResolution]
CONVERSION_RESOLUTION_QHD: _ClassVar[ConversionResolution]
CONVERSION_RESOLUTION_UHD: _ClassVar[ConversionResolution]
class NotesFormat(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
NOTES_FORMAT_UNSPECIFIED: _ClassVar[NotesFormat]
NOTES_FORMAT_PLAIN: _ClassVar[NotesFormat]
NOTES_FORMAT_HTML: _ClassVar[NotesFormat]
CONVERSION_STATUS_UNSPECIFIED: ConversionStatus
CONVERSION_STATUS_PENDING: ConversionStatus
CONVERSION_STATUS_RUNNING: ConversionStatus
@@ -52,6 +58,9 @@ CONVERSION_RESOLUTION_HD: ConversionResolution
CONVERSION_RESOLUTION_FHD: ConversionResolution
CONVERSION_RESOLUTION_QHD: ConversionResolution
CONVERSION_RESOLUTION_UHD: ConversionResolution
NOTES_FORMAT_UNSPECIFIED: NotesFormat
NOTES_FORMAT_PLAIN: NotesFormat
NOTES_FORMAT_HTML: NotesFormat
class JpegOutputOptions(_message.Message):
__slots__ = ("quality",)
@@ -67,17 +76,45 @@ class SlideRasterOptions(_message.Message):
jpeg: JpegOutputOptions
def __init__(self, resolution: _Optional[_Union[ConversionResolution, str]] = ..., jpeg: _Optional[_Union[JpegOutputOptions, _Mapping]] = ...) -> None: ...
class HtmlFormattingPolicy(_message.Message):
__slots__ = ("ignore_bold", "ignore_italic", "ignore_underline", "ignore_strikethrough", "ignore_font_size", "ignore_color")
IGNORE_BOLD_FIELD_NUMBER: _ClassVar[int]
IGNORE_ITALIC_FIELD_NUMBER: _ClassVar[int]
IGNORE_UNDERLINE_FIELD_NUMBER: _ClassVar[int]
IGNORE_STRIKETHROUGH_FIELD_NUMBER: _ClassVar[int]
IGNORE_FONT_SIZE_FIELD_NUMBER: _ClassVar[int]
IGNORE_COLOR_FIELD_NUMBER: _ClassVar[int]
ignore_bold: bool
ignore_italic: bool
ignore_underline: bool
ignore_strikethrough: bool
ignore_font_size: bool
ignore_color: bool
def __init__(self, ignore_bold: _Optional[bool] = ..., ignore_italic: _Optional[bool] = ..., ignore_underline: _Optional[bool] = ..., ignore_strikethrough: _Optional[bool] = ..., ignore_font_size: _Optional[bool] = ..., ignore_color: _Optional[bool] = ...) -> None: ...
class NotesOptions(_message.Message):
__slots__ = ("format", "html_use_paragraph_tags", "html_policy")
FORMAT_FIELD_NUMBER: _ClassVar[int]
HTML_USE_PARAGRAPH_TAGS_FIELD_NUMBER: _ClassVar[int]
HTML_POLICY_FIELD_NUMBER: _ClassVar[int]
format: NotesFormat
html_use_paragraph_tags: bool
html_policy: HtmlFormattingPolicy
def __init__(self, format: _Optional[_Union[NotesFormat, str]] = ..., html_use_paragraph_tags: _Optional[bool] = ..., html_policy: _Optional[_Union[HtmlFormattingPolicy, _Mapping]] = ...) -> None: ...
class Slide(_message.Message):
__slots__ = ("index", "notes_plain", "image_url", "thumbnail_image_url")
__slots__ = ("index", "notes_plain", "image_url", "thumbnail_image_url", "notes_html")
INDEX_FIELD_NUMBER: _ClassVar[int]
NOTES_PLAIN_FIELD_NUMBER: _ClassVar[int]
IMAGE_URL_FIELD_NUMBER: _ClassVar[int]
THUMBNAIL_IMAGE_URL_FIELD_NUMBER: _ClassVar[int]
NOTES_HTML_FIELD_NUMBER: _ClassVar[int]
index: int
notes_plain: str
image_url: str
thumbnail_image_url: str
def __init__(self, index: _Optional[int] = ..., notes_plain: _Optional[str] = ..., image_url: _Optional[str] = ..., thumbnail_image_url: _Optional[str] = ...) -> None: ...
notes_html: str
def __init__(self, index: _Optional[int] = ..., notes_plain: _Optional[str] = ..., image_url: _Optional[str] = ..., thumbnail_image_url: _Optional[str] = ..., notes_html: _Optional[str] = ...) -> None: ...
class SlideDeck(_message.Message):
__slots__ = ("conversion_id", "source_filename", "slides", "created_at", "width", "height", "thumbnail_width", "thumbnail_height")
@@ -100,14 +137,16 @@ class SlideDeck(_message.Message):
def __init__(self, conversion_id: _Optional[str] = ..., source_filename: _Optional[str] = ..., slides: _Optional[_Iterable[_Union[Slide, _Mapping]]] = ..., created_at: _Optional[_Union[datetime.datetime, _timestamp_pb2.Timestamp, _Mapping]] = ..., width: _Optional[int] = ..., height: _Optional[int] = ..., thumbnail_width: _Optional[int] = ..., thumbnail_height: _Optional[int] = ...) -> None: ...
class CreateConversionRequest(_message.Message):
__slots__ = ("source_filename", "full", "thumbnail")
__slots__ = ("source_filename", "full", "thumbnail", "notes")
SOURCE_FILENAME_FIELD_NUMBER: _ClassVar[int]
FULL_FIELD_NUMBER: _ClassVar[int]
THUMBNAIL_FIELD_NUMBER: _ClassVar[int]
NOTES_FIELD_NUMBER: _ClassVar[int]
source_filename: str
full: SlideRasterOptions
thumbnail: SlideRasterOptions
def __init__(self, source_filename: _Optional[str] = ..., full: _Optional[_Union[SlideRasterOptions, _Mapping]] = ..., thumbnail: _Optional[_Union[SlideRasterOptions, _Mapping]] = ...) -> None: ...
notes: NotesOptions
def __init__(self, source_filename: _Optional[str] = ..., full: _Optional[_Union[SlideRasterOptions, _Mapping]] = ..., thumbnail: _Optional[_Union[SlideRasterOptions, _Mapping]] = ..., notes: _Optional[_Union[NotesOptions, _Mapping]] = ...) -> None: ...
class CreateConversionResponse(_message.Message):
__slots__ = ("conversion_id", "upload_bucket", "upload_object_key", "upload_url", "expires_at")
@@ -68,6 +68,33 @@ message SlideRasterOptions {
}
}
// NotesFormat controls what note representation the server should compute.
enum NotesFormat {
NOTES_FORMAT_UNSPECIFIED = 0;
NOTES_FORMAT_PLAIN = 1;
NOTES_FORMAT_HTML = 2;
}
// HtmlFormattingPolicy configures which formatting features are ignored in HTML mode.
message HtmlFormattingPolicy {
bool ignore_bold = 1;
bool ignore_italic = 2;
bool ignore_underline = 3;
bool ignore_strikethrough = 4;
bool ignore_font_size = 5;
bool ignore_color = 6;
}
// NotesOptions configures note extraction behavior.
message NotesOptions {
// Output format for extracted notes.
NotesFormat format = 1;
// If true, wrap PPTX paragraphs as <p> blocks in HTML mode. If false, emit <br/> only.
optional bool html_use_paragraph_tags = 2;
// Formatting policy for HTML mode.
HtmlFormattingPolicy html_policy = 3;
}
// Slide contains extracted notes and the rendered image URL for one slide.
message Slide {
int32 index = 1;
@@ -76,6 +103,8 @@ message Slide {
string image_url = 3;
// Thumbnail rendered image URL.
string thumbnail_image_url = 4;
// Optional, sanitized HTML version of notes when requested.
string notes_html = 5;
}
// SlideDeck is the final structured conversion artifact.
@@ -100,6 +129,7 @@ message CreateConversionRequest {
string source_filename = 1;
SlideRasterOptions full = 2;
SlideRasterOptions thumbnail = 3;
NotesOptions notes = 4;
}
// CreateConversionResponse returns upload details for the session.
@@ -1,19 +1,27 @@
"""Public conversion APIs for the officeconvert Python library."""
from officeconvert.conversion import (
HtmlFormattingPolicy,
NotesFormat,
NotesOptions,
SlideArtifact,
SlideDeckResult,
convert_pptx_to_pdf,
convert_pptx_to_slidedeck,
extract_slide_notes,
extract_slide_notes_html,
render_pdf_to_images,
)
__all__ = [
"HtmlFormattingPolicy",
"NotesFormat",
"NotesOptions",
"SlideArtifact",
"SlideDeckResult",
"convert_pptx_to_pdf",
"convert_pptx_to_slidedeck",
"extract_slide_notes",
"extract_slide_notes_html",
"render_pdf_to_images",
]
@@ -4,6 +4,8 @@ from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from enum import Enum
import html
import logging
import math
from pathlib import Path
@@ -13,6 +15,28 @@ from typing import Iterable
from pptx import Presentation
class NotesFormat(str, Enum):
PLAIN = "plain"
HTML = "html"
@dataclass(frozen=True, slots=True)
class HtmlFormattingPolicy:
ignore_bold: bool = False
ignore_italic: bool = False
ignore_underline: bool = False
ignore_strikethrough: bool = False
ignore_font_size: bool = False
ignore_color: bool = False
@dataclass(frozen=True, slots=True)
class NotesOptions:
format: NotesFormat = NotesFormat.PLAIN
html_use_paragraph_tags: bool = True
html_policy: HtmlFormattingPolicy = HtmlFormattingPolicy()
@dataclass(frozen=True, slots=True)
class SlideArtifact:
"""Represents one converted slide image and its extracted notes."""
@@ -21,6 +45,7 @@ class SlideArtifact:
image_path: Path
thumbnail_path: Path
notes_plain: str
notes_html: str = ""
@dataclass(frozen=True, slots=True)
@@ -290,14 +315,52 @@ def extract_slide_notes(pptx_path: Path) -> list[str]:
if not pptx_path.exists():
raise FileNotFoundError(f"source PPTX does not exist: {pptx_path}")
plain, _html = _extract_slide_notes(pptx_path, options=NotesOptions(format=NotesFormat.PLAIN))
return plain
def extract_slide_notes_html(
pptx_path: Path,
*,
options: NotesOptions | None = None,
) -> list[str]:
"""Extract sanitized HTML notes for each slide in slide index order.
The returned HTML is sanitized-by-construction:
- text content is always escaped
- only a small allowlist of tags is emitted: p, br, strong, em, u, s, span
- only `style` attributes generated by this function are emitted on span tags
"""
if not pptx_path.exists():
raise FileNotFoundError(f"source PPTX does not exist: {pptx_path}")
resolved = options or NotesOptions(format=NotesFormat.HTML)
if resolved.format != NotesFormat.HTML:
raise ValueError("extract_slide_notes_html requires NotesOptions.format=NotesFormat.HTML")
_plain, html_notes = _extract_slide_notes(pptx_path, options=resolved)
return html_notes
def _extract_slide_notes(
pptx_path: Path,
*,
options: NotesOptions,
) -> tuple[list[str], list[str]]:
presentation = Presentation(str(pptx_path.resolve()))
notes: list[str] = []
want_html = options.format == NotesFormat.HTML
plain_notes: list[str] = []
html_notes: list[str] = []
for slide in presentation.slides:
if not slide.has_notes_slide:
notes.append("")
plain_notes.append("")
html_notes.append("")
continue
notes.append(_extract_notes_text(slide.notes_slide.shapes))
return notes
plain = _extract_notes_text(slide.notes_slide.shapes)
plain_notes.append(plain)
html_notes.append(_extract_notes_html(slide.notes_slide.shapes, options=options) if want_html else "")
return plain_notes, html_notes
def convert_pptx_to_slidedeck(
@@ -314,6 +377,7 @@ def convert_pptx_to_slidedeck(
pptx_to_pdf_per_slide_timeout_s: int = 3,
pdf_to_images_base_timeout_s: int = 30,
pdf_to_images_per_slide_timeout_s: int = 8,
notes_options: NotesOptions | None = None,
progress_callback: ProgressCallback | None = None,
) -> SlideDeckResult:
"""Convert a PPTX into rendered images and extracted notes.
@@ -344,8 +408,9 @@ def convert_pptx_to_slidedeck(
full_image_dir = work_dir / "slides_full"
thumbnail_image_dir = work_dir / "slides_thumb"
resolved_notes_options = notes_options or NotesOptions(format=NotesFormat.PLAIN)
_emit_progress(progress_callback, PHASE_EXTRACTING_NOTES, 0, 1)
notes = extract_slide_notes(pptx_path)
notes_plain, notes_html = _extract_slide_notes(pptx_path, options=resolved_notes_options)
_emit_progress(progress_callback, PHASE_EXTRACTING_NOTES, 1, 1)
slide_width, slide_height = _read_slide_size_emu(pptx_path)
output_width, output_height = _infer_output_dimensions_from_slide_size(
@@ -370,7 +435,7 @@ def convert_pptx_to_slidedeck(
output_width_px=thumbnail_width,
output_height_px=thumbnail_height,
)
slide_count = len(notes)
slide_count = len(notes_plain)
pptx_to_pdf_timeout = _compute_adaptive_timeout(
slide_count=slide_count,
timeout_cap_s=pptx_to_pdf_timeout_s,
@@ -454,15 +519,15 @@ def convert_pptx_to_slidedeck(
),
)
if len(full_image_paths) != len(notes):
if len(full_image_paths) != len(notes_plain):
raise ValueError(
"rendered full-size slide count does not match note count: "
f"{len(full_image_paths)} image(s) vs {len(notes)} note entries"
f"{len(full_image_paths)} image(s) vs {len(notes_plain)} note entries"
)
if len(thumbnail_image_paths) != len(notes):
if len(thumbnail_image_paths) != len(notes_plain):
raise ValueError(
"rendered thumbnail slide count does not match note count: "
f"{len(thumbnail_image_paths)} image(s) vs {len(notes)} note entries"
f"{len(thumbnail_image_paths)} image(s) vs {len(notes_plain)} note entries"
)
slides = [
@@ -470,10 +535,11 @@ def convert_pptx_to_slidedeck(
index=index,
image_path=image_path,
thumbnail_path=thumbnail_path,
notes_plain=note,
notes_plain=plain,
notes_html=html,
)
for index, (image_path, thumbnail_path, note) in enumerate(
zip(full_image_paths, thumbnail_image_paths, notes),
for index, (image_path, thumbnail_path, plain, html) in enumerate(
zip(full_image_paths, thumbnail_image_paths, notes_plain, notes_html),
start=1,
)
]
@@ -632,3 +698,102 @@ def _extract_notes_text(shapes: Iterable[object]) -> str:
if text:
segments.append(text)
return "\n\n".join(segments).strip()
def _extract_notes_html(shapes: Iterable[object], *, options: NotesOptions) -> str:
"""Extract sanitized HTML from note shapes while preserving paragraph boundaries."""
paragraphs_html: list[str] = []
for shape in shapes:
text_frame = getattr(shape, "text_frame", None)
if text_frame is None:
continue
for paragraph in getattr(text_frame, "paragraphs", []) or []:
paragraph_html = _paragraph_to_html(paragraph, options=options)
if paragraph_html:
paragraphs_html.append(paragraph_html)
if not paragraphs_html:
return ""
if options.html_use_paragraph_tags:
return "".join(paragraphs_html)
# Flatten paragraph boundaries into <br/> separators (double-break between paragraphs).
return "<br/><br/>".join(
p.removeprefix("<p>").removesuffix("</p>") if p.startswith("<p>") else p
for p in paragraphs_html
)
def _paragraph_to_html(paragraph: object, *, options: NotesOptions) -> str:
policy = options.html_policy
parts: list[str] = []
for run in getattr(paragraph, "runs", []) or []:
text = getattr(run, "text", "") or ""
if not text:
continue
# Escape first, then re-introduce only allowlisted tags.
escaped = html.escape(text, quote=False)
escaped = escaped.replace("\n", "<br/>")
font = getattr(run, "font", None)
style = ""
if font is not None:
if not policy.ignore_color:
rgb_obj = getattr(getattr(font, "color", None), "rgb", None)
if rgb_obj is not None:
rgb = str(rgb_obj)
if isinstance(rgb, str) and len(rgb) == 6:
style += f"color: #{rgb};"
if not policy.ignore_font_size:
size = getattr(font, "size", None)
pt = getattr(size, "pt", None)
if isinstance(pt, (int, float)):
# Clamp to a sane range to avoid pathological CSS.
if 0.5 <= pt <= 512:
pt_str = str(int(pt)) if float(pt).is_integer() else str(round(float(pt), 2))
style += f"font-size: {pt_str}pt;"
content = escaped
if style:
content = f"<span style=\"{style}\">{content}</span>"
if font is not None:
if not policy.ignore_bold and getattr(font, "bold", None) is True:
content = f"<strong>{content}</strong>"
if not policy.ignore_italic and getattr(font, "italic", None) is True:
content = f"<em>{content}</em>"
if not policy.ignore_underline and _truthy_underline(getattr(font, "underline", None)):
content = f"<u>{content}</u>"
if not policy.ignore_strikethrough and _truthy_strikethrough(run):
content = f"<s>{content}</s>"
parts.append(content)
inner = "".join(parts).strip()
if not inner:
return ""
if options.html_use_paragraph_tags:
return f"<p>{inner}</p>"
return inner
def _truthy_underline(value: object) -> bool:
# python-pptx may represent underline as True/False/None or an enum value.
if value is True:
return True
if value in (None, False):
return False
# Any non-falsey, non-None value implies underline style.
return True
def _truthy_strikethrough(run: object) -> bool:
# python-pptx doesn't currently expose a first-class Font.strike property; detect via XML.
xml_run = getattr(run, "_r", None)
rpr = getattr(xml_run, "rPr", None)
if rpr is None:
return False
strike_val = rpr.get("strike")
if strike_val in (None, "noStrike", "false", "0"):
return False
return True
@@ -0,0 +1,111 @@
import tempfile
import unittest
from pathlib import Path
from pptx import Presentation
from pptx.dml.color import RGBColor
from pptx.util import Pt
from officeconvert.conversion import HtmlFormattingPolicy, NotesFormat, NotesOptions, extract_slide_notes_html
def _build_pptx_with_formatted_notes(tmp_path: Path) -> Path:
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[5]) # blank
notes = slide.notes_slide
tf = notes.notes_text_frame
# First paragraph: mixed runs with formatting.
p1 = tf.paragraphs[0]
p1.text = ""
r1 = p1.add_run()
r1.text = "BoldRed24 "
r1.font.bold = True
r1.font.color.rgb = RGBColor(0xFF, 0x00, 0x00)
r1.font.size = Pt(24)
r2 = p1.add_run()
r2.text = "UnderStrike"
r2.font.underline = True
# python-pptx does not currently expose a first-class API for strikethrough,
# but the underlying DrawingML uses a:rPr@strike="sngStrike" or "dblStrike".
r2._r.rPr.set("strike", "sngStrike")
# Second paragraph: ensure escaping is applied.
p2 = tf.add_paragraph()
r3 = p2.add_run()
r3.text = "<script>alert(1)</script>"
r3.font.italic = True
out = tmp_path / "notes.pptx"
prs.save(out)
return out
class NotesHtmlExtractionTests(unittest.TestCase):
def test_extracts_basic_rich_text_and_escapes(self) -> None:
with tempfile.TemporaryDirectory() as d:
pptx_path = _build_pptx_with_formatted_notes(Path(d))
html_notes = extract_slide_notes_html(
pptx_path,
options=NotesOptions(format=NotesFormat.HTML, html_use_paragraph_tags=True),
)
self.assertEqual(len(html_notes), 1)
html_out = html_notes[0]
# Paragraphs are wrapped in <p>.
self.assertIn("<p>", html_out)
self.assertIn("</p>", html_out)
# Formatting tags.
self.assertIn("<strong>", html_out)
self.assertIn("<em>", html_out)
self.assertIn("<u>", html_out)
self.assertIn("<s>", html_out)
# Style tags for RGB and font size.
self.assertIn('style="color: #FF0000;font-size: 24pt;"', html_out)
# Escaping: no raw <script> tag survives.
self.assertNotIn("<script>", html_out)
self.assertIn("&lt;script&gt;alert(1)&lt;/script&gt;", html_out)
def test_ignore_policy_prunes_styles_and_tags(self) -> None:
with tempfile.TemporaryDirectory() as d:
pptx_path = _build_pptx_with_formatted_notes(Path(d))
html_notes = extract_slide_notes_html(
pptx_path,
options=NotesOptions(
format=NotesFormat.HTML,
html_use_paragraph_tags=True,
html_policy=HtmlFormattingPolicy(
ignore_bold=True,
ignore_color=True,
ignore_font_size=True,
),
),
)
html_out = html_notes[0]
self.assertNotIn("<strong>", html_out)
self.assertNotIn("color:", html_out)
self.assertNotIn("font-size:", html_out)
def test_br_mode_uses_double_break_between_paragraphs(self) -> None:
with tempfile.TemporaryDirectory() as d:
pptx_path = _build_pptx_with_formatted_notes(Path(d))
html_notes = extract_slide_notes_html(
pptx_path,
options=NotesOptions(format=NotesFormat.HTML, html_use_paragraph_tags=False),
)
html_out = html_notes[0]
self.assertNotIn("<p>", html_out)
self.assertIn("<br/><br/>", html_out)
if __name__ == "__main__":
unittest.main()
@@ -23,6 +23,7 @@ class ConversionSession:
thumbnail_resolution: conversion_pb2.ConversionResolution
full_jpeg_quality: int
thumbnail_jpeg_quality: int
notes: conversion_pb2.NotesOptions | None = None
bucket_name: str
upload_object_key: str
status: conversion_pb2.ConversionStatus
@@ -18,6 +18,9 @@ from google.protobuf.timestamp_pb2 import Timestamp
from officeconvert import SlideArtifact, convert_pptx_to_slidedeck
from officeconvert.conversion import (
ConversionTimeoutError,
HtmlFormattingPolicy,
NotesFormat,
NotesOptions,
PHASE_EXTRACTING_NOTES,
PHASE_PDF_TO_IMAGES,
PHASE_PPTX_TO_PDF,
@@ -49,6 +52,37 @@ _DEFAULT_FULL_RESOLUTION = conversion_pb2.CONVERSION_RESOLUTION_FHD
_DEFAULT_THUMBNAIL_RESOLUTION = conversion_pb2.CONVERSION_RESOLUTION_SD
_DEFAULT_FULL_JPEG_QUALITY = 85
_DEFAULT_THUMBNAIL_JPEG_QUALITY = 75
_DEFAULT_NOTES_FORMAT = conversion_pb2.NOTES_FORMAT_PLAIN
_DEFAULT_HTML_USE_PARAGRAPH_TAGS = True
def _to_library_notes_options(
notes: conversion_pb2.NotesOptions | None,
) -> NotesOptions | None:
if notes is None:
return None
fmt = notes.format or _DEFAULT_NOTES_FORMAT
library_format = NotesFormat.HTML if fmt == conversion_pb2.NOTES_FORMAT_HTML else NotesFormat.PLAIN
html_use_paragraph_tags = _DEFAULT_HTML_USE_PARAGRAPH_TAGS
if notes.HasField("html_use_paragraph_tags"):
html_use_paragraph_tags = bool(notes.html_use_paragraph_tags)
policy_proto = notes.html_policy
policy = HtmlFormattingPolicy(
ignore_bold=bool(policy_proto.ignore_bold),
ignore_italic=bool(policy_proto.ignore_italic),
ignore_underline=bool(policy_proto.ignore_underline),
ignore_strikethrough=bool(policy_proto.ignore_strikethrough),
ignore_font_size=bool(policy_proto.ignore_font_size),
ignore_color=bool(policy_proto.ignore_color),
)
return NotesOptions(
format=library_format,
html_use_paragraph_tags=html_use_paragraph_tags,
html_policy=policy,
)
class ConversionServiceImpl(conversion_connect.ConversionService):
@@ -124,6 +158,7 @@ class ConversionServiceImpl(conversion_connect.ConversionService):
thumbnail_resolution=thumbnail_resolution,
full_jpeg_quality=full_jpeg_quality,
thumbnail_jpeg_quality=thumbnail_jpeg_quality,
notes=request.notes if request.HasField("notes") else None,
bucket_name=bucket_name,
upload_object_key=upload_key,
status=conversion_pb2.CONVERSION_STATUS_PENDING,
@@ -280,6 +315,7 @@ class ConversionServiceImpl(conversion_connect.ConversionService):
pptx_to_pdf_per_slide_timeout_s=self._config.conversion_pptx_to_pdf_per_slide_timeout_seconds,
pdf_to_images_base_timeout_s=self._config.conversion_pdf_to_images_base_timeout_seconds,
pdf_to_images_per_slide_timeout_s=self._config.conversion_pdf_to_images_per_slide_timeout_seconds,
notes_options=_to_library_notes_options(session.notes),
progress_callback=lambda phase_name, current, max_value: self._set_session_progress_from_name(
session,
phase_name=phase_name,
@@ -428,6 +464,7 @@ class ConversionServiceImpl(conversion_connect.ConversionService):
conversion_pb2.Slide(
index=slide.index,
notes_plain=slide.notes_plain,
notes_html=slide.notes_html,
image_url=image_url,
thumbnail_image_url=thumbnail_image_url,
)