Browse our Products
Aspose.Words for Python via .NET 24.6 Release Notes
Major Features
There are 77 improvements and fixes in this regular monthly release. The most notable are:
- Enhanced Charting Capabilities: Now you can create a wider variety of charts, including Treemaps, Sunbursts, Histograms, Pareto charts, Box & Whisker charts, Waterfalls, and Funnels. This allows for a more diverse and informative way to visualize your data.
- Color Control for Shadow Formatting: Gain finer control over the visual appearance of your documents by accessing the shadow colors.
- Advanced Comparison Options: Streamline your data analysis workflows with improved comparison functionality. This includes the new “IgnoreStoreItemId” option and a redesigned interface for advanced comparisons.
- Enhanced Comment Management: The new Comment.DateTimeUtc property provides a more accurate timestamp for comments, improving organization and traceability.
- Performance Boost for Background Rendering: Experience significantly faster rendering times for backgrounds containing small elements thanks to native tiling technology.
- Realistic Gradients for Shapes: Create DML shapes with nonlinear gradients, mimicking the visual style of MS Word for a more polished look.
Full List of Issues Covering all Changes in this Release
Expand to view the full list of issues.
- Aspose.Words.UnsupportedFileFormatException “Unknown file format.”
- Consider providing an option to take in account only SDT content upon conparing
- Add possibility to insert new ChartEx using DocumentBuilder.InsertChart method
- Allow creation of Histogram (Pareto) charts
- Consider providing access to comment date stored in commentsExtensible.xml
- Consider supporting of min/max-width & min/max-height CSS properties
- Implement native tile rendering of textured background shape
- Add support to get DML effects of a Shape|Enhancement
- Left indent of first list item is incorrect after import from HTML
- Incorrect revisions after comparing documents
- InitializeContextStep throws StackOverflowException
- Password protection issue with Unprotect
- Document.Unprotect(string) still unprotects password protected document if empty string or null is passed as a password
- FileCorruptedException is thrown upon loading DOC document
- Missed glyphs are rendered if PdfSaveOptions.PreserveFormFields is enabled
- Pdf2Word. PdfTilingPatternFactory throws CantCreateBitmapException
- UnsupportedFileFormatException is thrown upon loading DOC file
- Aspose.Words.UnsupportedFileFormatException is thrown while loading DOC
- Aspose.Words hangs when saving DOC to JPEG
- DOC file is loaded as TXT produces garbage characters in the output
- InvalidOperationException, cannot parse Pages entry
- SVG is rendered improperly
- Left indent of list item is incorrect after conversion DOCX to HTML
- Metafile is blurred after converting RTF to HTML
- ColontitleBookmarksCorrector throws NullReferenceException
- Order of style aliases changes after resaving a DOCX document
- getAdjustments() returns empty collection for adjusted shape when adj == 0
- CheckFormatStep throws ArgumentOutOfRangeException
- HTML fixed from Word does not match
- MHTML to PDF: Save method hangs
- Slow rendering of page background composed of tiled 1x1 pixel image
- Rendering DOCX to PDF never finishes
- Aspose.Words hangs upon rendering MHTML document
- Aspose.Words hangs upon rendering document
- Font is changed after open/save document
- Incorrect line wrapping in a justified paragraph on conversion to PDF
- Text it table is wrapped improperly upon rendering
- Interscript spacing issue when a paragraph is justified
- DOCX to PDF: Chinese characters not rendered correctly
- InvalidOperationException is thrown upon building report
- An extra page on RTF to PDF conversion
- InvalidCastException is thrown upon rendering document with ShowInBalloons.Format
- Numeration items count and formatting changed after conversion
- PNG images produced by Aspose.Words does not pass validation.
- Extra paragraph added after SetMapping
- Different page margins are reported by AW model and MS Word
- Aspose.Words hangs upon rendering document
- The offsets before and after equality operators are different from offsets of other operators
- Replace settings of destination blank document in Merger
- RTF to PDF Conversion Issue: content flow across pages does not match MS Word
- Can not save the DOCX source to DOC format
- Fill color modifiers have incorrect coefficients
- Error during conversion to svg with set MaxImageResolution option
- DOCX merging issue
- NullReferenceException is thrown upon extracting page
- Consider attach a property to get/set noPunctuationKerning option
- DML shapes filled with a two-color gradient are rendered as linear gradients
- Style names are changed after saving DOCX to HTML to DOCX
- EQ field is rendered improperly
- OleFormat.IconCaption getter throws ArgumentOutOfRangeException
- Unable to substitute a missing font with a font set up as a default font
- Metafile colors are darker after rendering in .NET Standard and Java
- Compare result does not match MS Word output
- InvalidOperationException is thrown upon updating page layout after changing page setup
- Aspose.Words detects changes in shapes upon comparing documents
- Text is shifted right after rendering
- Clearing CustomXmlParts resets the style on component
- Comparing document to itself produces revisions
- Object reference not set to an instance exception when compare 2 specific documents
- StackOverflowException is thrown upon converting RTF to HTML
- DOCX to PDF: Page alignment issues
- DOCX to PDF conversion bug
- Inconsistencies when inserting OLE object in DOCX
- Soft edge effect is not rendered in PDF/A by Aspose.Words
- Compare result does not match MS Word output
- Redundant revision on field after comparing documents
- Text position changed after merge
Public API and Backward Incompatible Changes
This section lists public API changes that were introduced in Aspose.Words for Python via .NET 24.6. It includes not only new and obsoleted public methods, but also a description of any changes in the behavior behind the scenes in Aspose.Words for Python via .NET which may affect existing code. Any behavior introduced that could be seen as regression and modifies the existing behavior is especially important and is documented here.
Added ability to create Treemap, Sunburst, Histogram, Pareto, Box & Whisker, Waterfall and Funnel charts
An ability to create TREEMAP, SUNBURST, HISTOGRAM, PARETO, BOX_AND_WHISKER, WATERFALL and FUNNEL charts has been implemented. The corresponding items are added to the ChartType enumeration type. Additional methods are provided in the ChartSeriesCollection class for creating series of the new chart types.
This use case explains how to create a BOX_AND_WHISKER chart:
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
# Insert a Box & Whisker chart.
shape = builder.insert_chart(chart_type=aw.drawing.charts.ChartType.BOX_AND_WHISKER, width=450, height=450)
chart = shape.chart
chart.title.text = 'Points by Years'
# Delete default generated series.
chart.series.clear()
# Add a series.
series = chart.series.add(series_name='Points by Years', categories=['WC', 'WC', 'WC', 'WC', 'WC', 'WC', 'WC', 'WC', 'WC', 'WC', 'NR', 'NR', 'NR', 'NR', 'NR', 'NR', 'NR', 'NR', 'NR', 'NR', 'NA', 'NA', 'NA', 'NA', 'NA', 'NA', 'NA', 'NA', 'NA', 'NA'], values=[91, 80, 100, 77, 90, 104, 105, 118, 120, 101, 114, 107, 110, 60, 79, 78, 77, 102, 101, 113, 94, 93, 84, 71, 80, 103, 80, 94, 100, 101])
# Show data labels.
series.has_data_labels = True
doc.save(file_name=ARTIFACTS_DIR + 'Charts.BoxAndWhisker.docx')
This use case explains how create a FUNNEL chart:
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
# Insert a Funnel chart.
shape = builder.insert_chart(chart_type=aw.drawing.charts.ChartType.FUNNEL, width=450, height=450)
chart = shape.chart
chart.title.text = 'Population by Age Group'
# Delete default generated series.
chart.series.clear()
# Add a series.
series = chart.series.add(series_name='Population by Age Group', categories=['0-9', '10-19', '20-29', '30-39', '40-49', '50-59', '60-69', '70-79', '80-89', '90-'], values=[0.121, 0.128, 0.132, 0.146, 0.124, 0.124, 0.111, 0.075, 0.032, 0.007])
# Show data labels.
series.has_data_labels = True
decimal_separator = locale.localeconv()['decimal_point']
series.data_labels.number_format.format_code = f"0{decimal_separator}0%"
doc.save(file_name=ARTIFACTS_DIR + 'Charts.Funnel.docx')
This use case explains how to create a HISTOGRAM chart:
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
# Insert a Histogram chart.
shape = builder.insert_chart(chart_type=aw.drawing.charts.ChartType.HISTOGRAM, width=450, height=450)
chart = shape.chart
chart.title.text = 'Avg Temperature since 1991'
# Delete default generated series.
chart.series.clear()
# Add a series.
chart.series.add(series_name='Avg Temperature', x_values=[51.8, 53.6, 50.3, 54.7, 53.9, 54.3, 53.4, 52.9, 53.3, 53.7, 53.8, 52, 55, 52.1, 53.4, 53.8, 53.8, 51.9, 52.1, 52.7, 51.8, 56.6, 53.3, 55.6, 56.3, 56.2, 56.1, 56.2, 53.6, 55.7, 56.3, 55.9, 55.6])
doc.save(file_name=ARTIFACTS_DIR + 'Charts.Histogram.docx')
This use case explains how to create a PARETO chart:
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
# Insert a Pareto chart.
shape = builder.insert_chart(chart_type=aw.drawing.charts.ChartType.PARETO, width=450, height=450)
chart = shape.chart
chart.title.text = 'Best-Selling Car'
# Delete default generated series.
chart.series.clear()
# Add a series.
chart.series.add(series_name='Best-Selling Car', categories=['Tesla Model Y', 'Toyota Corolla', 'Toyota RAV4', 'Ford F-Series', 'Honda CR-V'], values=[1.43, 0.91, 1.17, 0.98, 0.85])
doc.save(file_name=ARTIFACTS_DIR + 'Charts.Pareto.docx')
This use case explains how to create a SUNBURST chart:
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
# Insert a Sunburst chart.
shape = builder.insert_chart(chart_type=aw.drawing.charts.ChartType.SUNBURST, width=450, height=450)
chart = shape.chart
chart.title.text = 'Sales'
# Delete default generated series.
chart.series.clear()
# Add a series.
series = chart.series.add_multilevel_value(series_name='Sales', categories=[aw.drawing.charts.ChartMultilevelValue(level1='Sales - Europe', level2='UK', level3='London Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - Europe', level2='UK', level3='Liverpool Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - Europe', level2='UK', level3='Manchester Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - Europe', level2='France', level3='Paris Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - Europe', level2='France', level3='Lyon Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - NA', level2='USA', level3='Denver Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - NA', level2='USA', level3='Seattle Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - NA', level2='USA', level3='Detroit Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - NA', level2='USA', level3='Houston Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - NA', level2='Canada', level3='Toronto Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - NA', level2='Canada', level3='Montreal Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - Oceania', level2='Australia', level3='Sydney Dep.'), aw.drawing.charts.ChartMultilevelValue(level1='Sales - Oceania', level2='New Zealand', level3='Auckland Dep.')], values=[1236, 851, 536, 468, 179, 527, 799, 1148, 921, 457, 482, 761, 694])
# Show data labels.
series.has_data_labels = True
series.data_labels.show_value = False
series.data_labels.show_category_name = True
doc.save(file_name=ARTIFACTS_DIR + 'Charts.Sunburst.docx')
This use case explains how to create a TREEMAP chart:
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
# Insert a Treemap chart.
shape = builder.insert_chart(chart_type=aw.drawing.charts.ChartType.TREEMAP, width=450, height=280)
chart = shape.chart
chart.title.text = 'World Population'
# Delete default generated series.
chart.series.clear()
# Add a series.
series = chart.series.add_multilevel_value(series_name='Population by Region', categories=[aw.drawing.charts.ChartMultilevelValue(level1='Asia', level2='China'), aw.drawing.charts.ChartMultilevelValue(level1='Asia', level2='India'), aw.drawing.charts.ChartMultilevelValue(level1='Asia', level2='Indonesia'), aw.drawing.charts.ChartMultilevelValue(level1='Asia', level2='Pakistan'), aw.drawing.charts.ChartMultilevelValue(level1='Asia', level2='Bangladesh'), aw.drawing.charts.ChartMultilevelValue(level1='Asia', level2='Japan'), aw.drawing.charts.ChartMultilevelValue(level1='Asia', level2='Philippines'), aw.drawing.charts.ChartMultilevelValue(level1='Asia', level2='Other'), aw.drawing.charts.ChartMultilevelValue(level1='Africa', level2='Nigeria'), aw.drawing.charts.ChartMultilevelValue(level1='Africa', level2='Ethiopia'), aw.drawing.charts.ChartMultilevelValue(level1='Africa', level2='Egypt'), aw.drawing.charts.ChartMultilevelValue(level1='Africa', level2='Other'), aw.drawing.charts.ChartMultilevelValue(level1='Europe', level2='Russia'), aw.drawing.charts.ChartMultilevelValue(level1='Europe', level2='Germany'), aw.drawing.charts.ChartMultilevelValue(level1='Europe', level2='Other'), aw.drawing.charts.ChartMultilevelValue(level1='Latin America', level2='Brazil'), aw.drawing.charts.ChartMultilevelValue(level1='Latin America', level2='Mexico'), aw.drawing.charts.ChartMultilevelValue(level1='Latin America', level2='Other'), aw.drawing.charts.ChartMultilevelValue(level1='Northern America', level2='United States'), aw.drawing.charts.ChartMultilevelValue(level1='Northern America', level2='Other'), aw.drawing.charts.ChartMultilevelValue(level1='Oceania')], values=[1409670000, 1400744000, 279118866, 241499431, 169828911, 123930000, 112892781, 764000000, 223800000, 107334000, 105914499, 903000000, 146150789, 84607016, 516000000, 203080756, 129713690, 310000000, 335893238, 35000000, 42000000])
# Show data labels.
series.has_data_labels = True
series.data_labels.show_value = True
series.data_labels.show_category_name = True
decimal_separator = locale.localeconv()['decimal_point']
series.data_labels.number_format.format_code = f"0{decimal_separator}0%"
doc.save(file_name=ARTIFACTS_DIR + 'Charts.Treemap.docx')
This use case explains how to create a WATERFALL chart:
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
# Insert a Waterfall chart.
shape = builder.insert_chart(chart_type=aw.drawing.charts.ChartType.WATERFALL, width=450, height=450)
chart = shape.chart
chart.title.text = 'New Zealand GDP'
# Delete default generated series.
chart.series.clear()
# Add a series.
series = chart.series.add(series_name='New Zealand GDP', categories=['2018', '2019 growth', '2020 growth', '2020', '2021 growth', '2022 growth', '2022'], values=[100, 0.57, -0.25, 100.32, 20.22, -2.92, 117.62], is_subtotal=[True, False, False, True, False, False, True])
# Show data labels.
series.has_data_labels = True
doc.save(file_name=ARTIFACTS_DIR + 'Charts.Waterfall.docx')
Added an ability to get a color of ShapeBase.ShadowFormat
Implemented new color public property in Aspose.Words.Drawing.ShadowFormat class:
This use case explains how get ShadowFormat color:
doc = aw.Document(file_name=MY_DIR + 'Shadow color.docx')
shape = doc.get_child(aw.NodeType.SHAPE, 0, True).as_shape()
self.assertEqual(aspose.pydrawing.Color.red.to_argb(), shape.shadow_format.color.to_argb())
Added new comparison option ignore_store_item_id and advanced comparison option redesing
New AdvancedCompareOptions class has beend added, new AdvancedCompareOptions.ignore_store_item_id option property has been added and option ignore_dml_unique_id property previously located in CompareOptions class has been moved to the newly created AdvancedCompareOptions class.
New ignore_store_item_id option allows to control case when content of StructuredDocumentTag being compared is the same but StructuredDocumentTags itself have a difference in internal details, in this case this is StructuredDocumentTag.xml_mapping.store_item_id difference.
Microsoft Word considers these SDTs to be different for any reason and this might confuse customers because they see that equal content is marked with edit revisions. By default we mimic MS Word here and this option allows to control the logic and produce more logical and readable comparison result.
Additionally we decided to extract all these experimental options into separate AdvancedCompareOptions class to avoid having mess. All options that have no equivalence in Microsoft Word will be published in AdvancedCompareOptions class. Thats why ignore_dml_unique_id property previously located in CompareOptions class was also moved into AdvancedCompareOptions class.
This use case explains how to use AdvancedCompareOptions:
doc_a = aw.Document(file_name=MY_DIR + 'Document with SDT 1.docx')
doc_b = aw.Document(file_name=MY_DIR + 'Document with SDT 2.docx')
# Configure options to compare SDT with same content but different store item id.
compare_options = aw.comparing.CompareOptions()
compare_options.advanced_options.ignore_store_item_id = False
doc_a.compare(doc_b, "user", datetime.now(), compare_options)
self.assertEqual(8, doc_a.revisions.count)
compare_options.advanced_options.ignore_store_item_id = True
doc_a.revisions.reject_all()
doc_a.compare(doc_b, "user", datetime.now(), compare_options)
self.assertEqual(0, doc_a.revisions.count)
Added public property Comment.date_time_utc
An ability to get comment UTC datetime has been implemented. If a comment does not contain such information, a value of the Comment.date_time property converted to UTC time is returned.
This use case explains how to get comment UTC datetime:
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
date = datetime.now()
comment = aw.Comment(doc, 'John Doe', 'J.D.', date)
comment.set_text('My comment.')
builder.current_paragraph.append_child(comment)
doc.save(file_name=ARTIFACTS_DIR + 'Comment.UtcDateTime.docx')
doc = aw.Document(ARTIFACTS_DIR + 'Comment.UtcDateTime.docx')
comment = doc.get_child(aw.NodeType.COMMENT, 0, True).as_comment()
# DateTimeUtc return data without milliseconds.
self.assertEqual(date.astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M:%S"), comment.date_time_utc.strftime("%Y-%m-%d %H:%M:%S"))