From 12d15d9e035903db127b3caa63a5ee1d57385c2c Mon Sep 17 00:00:00 2001 From: AshReo <33994521+ashreo@users.noreply.github.com> Date: Fri, 1 Aug 2025 10:41:41 +0800 Subject: [PATCH 1/4] Enhanced text features: multi-style, shadow, and background support Enhanced text features: multi-style, shadow, and background support - Added text multi-style support with TextStyleRange - Implemented text shadow effects with configurable parameters - Added text background with various style options - Updated example.py with comprehensive test cases - Fixed import issues in add_text_impl.py --- example.py | 182 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 164 insertions(+), 18 deletions(-) diff --git a/example.py b/example.py index 33141b2..1dba5bb 100644 --- a/example.py +++ b/example.py @@ -11,6 +11,7 @@ from pyJianYingDraft.text_segment import TextStyleRange, Text_style, Text_border # Base URL of the service, please modify according to actual situation BASE_URL = f"http://localhost:{PORT}" +LICENSE_KEY = "trial" # Trial license key def make_request(endpoint, data, method='POST'): """Send HTTP request to the server and handle the response""" url = f"{BASE_URL}/{endpoint}" @@ -68,7 +69,7 @@ def add_text_impl(text, start, end, font, font_color, font_size, track_name, dra width=1080, height=1920, fixed_width=-1, fixed_height=-1, text_styles=None): - """add text""" + """Add text with support for multiple styles, shadows, and backgrounds""" data = { "draft_folder": draft_folder, "text": text, @@ -360,7 +361,7 @@ def test_effect_01(): def test_text(): - """Test adding text""" + """Test adding text with various features""" draft_folder = "/Users/sunguannan/Movies/JianyingPro/User Data/Projects/com.lveditor.draft" # Test case 1: Basic text addition @@ -406,7 +407,7 @@ def test_text(): track_name="main_text", transform_y=0.0, transform_x=0.5, - border_color="#FF0000", # Black border + border_color="#FF0000", # Red border border_width=20.0, border_alpha=1.0, background_color="#0000FF", # Blue background @@ -415,13 +416,149 @@ def test_text(): ) print("Test case 3 (Border and background) successful:", result3) + # Test case 4: Text with shadow effect + result4 = add_text_impl( + draft_id=result3['output']['draft_id'], + text="Shadow effect test", + start=9, + end=12, + font="思源中宋", + font_color="#FFFFFF", # White text + font_size=28.0, + track_name="main_text", + transform_y=-0.3, + transform_x=0.5, + shadow_enabled=True, # Enable shadow + shadow_alpha=0.8, + shadow_angle=-30.0, + shadow_color="#000000", # Black shadow + shadow_distance=8.0, + shadow_smoothing=0.2 + ) + print("Test case 4 (Shadow effect) successful:", result4) + + # Test case 5: Multi-style text using TextStyleRange + # Create different text styles + style1 = { + "start": 0, + "end": 5, + "style": { + "color": "#FF0000", # Red + "size": 30, + "bold": True + }, + "border": { + "color": "#FFFFFF", # White border + "width": 40, + "alpha": 1.0 + }, + "font": "思源中宋" + } + + style2 = { + "start": 5, + "end": 10, + "style": { + "color": "#00FF00", # Green + "size": 25, + "italic": True + }, + "font": "挥墨体" + } + + style3 = { + "start": 10, + "end": 15, + "style": { + "color": "#0000FF", # Blue + "size": 20, + "underline": True + }, + "font": "金陵体" + } + + # Add multi-style text + result5 = add_text_impl( + draft_id=result4['output']['draft_id'], + text="Multi-style text test", + start=12, + end=15, + font="思源粗宋", + track_name="main_text", + transform_y=0.3, + transform_x=0.5, + font_color="#000000", # Default black + font_size=20.0, + # Use dictionary list instead of TextStyleRange object list + text_styles=[style1, style2, style3] + ) + print("Test case 5 (Multi-style text) successful:", result5) + + # Test case 6: Combined effects - shadow + background + multi-style + combined_style1 = { + "start": 0, + "end": 8, + "style": { + "color": "#FFD700", # Gold + "size": 32, + "bold": True + }, + "border": { + "color": "#8B4513", # Brown border + "width": 30, + "alpha": 0.8 + }, + "font": "思源中宋" + } + + combined_style2 = { + "start": 8, + "end": 16, + "style": { + "color": "#FF69B4", # Hot pink + "size": 28, + "italic": True + }, + "font": "挥墨体" + } + + result6 = add_text_impl( + draft_id=result5['output']['draft_id'], + text="Combined effects demo", + start=15, + end=18, + font="思源粗宋", + track_name="main_text", + transform_y=-0.6, + transform_x=0.5, + font_color="#FFFFFF", # Default white + font_size=24.0, + # Background settings + background_color="#4169E1", # Royal blue background + background_alpha=0.6, + background_style=1, + background_round_radius=0.3, + background_height=0.18, + background_width=0.8, + # Shadow settings + shadow_enabled=True, + shadow_alpha=0.7, + shadow_angle=-60.0, + shadow_color="#2F4F4F", # Dark slate gray shadow + shadow_distance=6.0, + shadow_smoothing=0.25, + # Multi-style text + text_styles=[combined_style1, combined_style2] + ) + print("Test case 6 (Combined effects) successful:", result6) + # Finally save and upload the draft - if result3.get('success') and result3.get('output'): - save_result = save_draft_impl(result3['output']['draft_id'],draft_folder) + if result6.get('success') and result6.get('output'): + save_result = save_draft_impl(result6['output']['draft_id'], draft_folder) print(f"Draft save result: {save_result}") # Return the last test result for subsequent operations (if any) - return result3 + return result6 def test_text_02(): @@ -854,8 +991,10 @@ def test_mask_01(): def test_mask_02(): """Test adding videos to different tracks""" + # Set draft folder path for saving draft_folder = "/Users/sunguannan/Movies/JianyingPro/User Data/Projects/com.lveditor.draft" - video_url = "https://cdn.wanx.aliyuncs.com/wanx/1719234057367822001/text_to_video/092faf3c94244973ab752ee1280ba76f.mp4?spm=5176.29623064.0.0.41ed26d6cBOhV3&file=092faf3c94244973ab752ee1280ba76f.mp4" # Replace with actual video URL + # Define video URL for testing + video_url = "https://cdn.wanx.aliyuncs.com/wanx/1719234057367822001/text_to_video/092faf3c94244973ab752ee1280ba76f.mp4?spm=5176.29623064.0.0.41ed26d6cBOhV3&file=092faf3c94244973ab752ee1280ba76f.mp4" draft_id = None # Initialize draft_id # Add video to first track @@ -1847,7 +1986,9 @@ def test_subtitle_02(): return text_result -def test_video(): +def test_video_01(): + """Test adding single video with transform and speed parameters""" + # Set draft folder path for saving draft_folder = "/Users/sunguannan/Movies/JianyingPro/User Data/Projects/com.lveditor.draft" print("\nTest: Adding video") @@ -1874,6 +2015,8 @@ def test_video(): save_draft_impl(video_result['output']['draft_id'], draft_folder) def test_video_02(): + """Test adding multiple videos with different resolutions to the same draft""" + # Set draft folder path for saving draft_folder = "/Users/sunguannan/Movies/JianyingPro/User Data/Projects/com.lveditor.draft" print("\nTest: Adding video") @@ -1911,7 +2054,7 @@ def test_video_02(): relative_index=0 ) video_result = add_video_impl( - video_url="https://videos.pexels.com/video-files/3129769/3129769-uhd_3840_2160_30fps.mp4", # 替换为实际的视频URL + video_url="https://videos.pexels.com/video-files/3129769/3129769-uhd_3840_2160_30fps.mp4", # Replace with actual video URL draft_id=video_result['output']['draft_id'], start=0, end=5, @@ -1927,7 +2070,7 @@ def test_video_02(): relative_index=0 ) video_result = add_video_impl( - video_url="https://videos.pexels.com/video-files/3129769/3129769-sd_426_240_30fps.mp4", # 替换为实际的视频URL + video_url="https://videos.pexels.com/video-files/3129769/3129769-sd_426_240_30fps.mp4", # Replace with actual video URL draft_id=video_result['output']['draft_id'], start=0, end=5, @@ -1943,7 +2086,7 @@ def test_video_02(): relative_index=0 ) video_result = add_video_impl( - video_url="https://videos.pexels.com/video-files/3129769/3129769-sd_640_360_30fps.mp4", # 替换为实际的视频URL + video_url="https://videos.pexels.com/video-files/3129769/3129769-sd_640_360_30fps.mp4", # Replace with actual video URL draft_id=video_result['output']['draft_id'], start=0, end=5, @@ -1959,7 +2102,7 @@ def test_video_02(): relative_index=0 ) video_result = add_video_impl( - video_url="https://videos.pexels.com/video-files/3129769/3129769-uhd_2560_1440_30fps.mp4", # 替换为实际的视频URL + video_url="https://videos.pexels.com/video-files/3129769/3129769-uhd_2560_1440_30fps.mp4", # Replace with actual video URL draft_id=video_result['output']['draft_id'], start=0, end=5, @@ -2064,7 +2207,8 @@ def test_stiker_03(): def test_transition_01(): - """Test adding multiple images""" + """Test adding multiple images with dissolve transition effects""" + # Set draft folder path for saving draft_folder = "/Users/sunguannan/Movies/JianyingPro/User Data/Projects/com.lveditor.draft" print("\nTest: Adding image 1") @@ -2103,9 +2247,11 @@ def test_transition_01(): def test_transition_02(): - """Test adding video tracks""" + """Test adding video tracks with transition effects""" + # Set draft folder path for saving draft_folder = "/Users/sunguannan/Movies/JianyingPro/User Data/Projects/com.lveditor.draft" - video_url = "https://cdn.wanx.aliyuncs.com/wanx/1719234057367822001/text_to_video/092faf3c94244973ab752ee1280ba76f.mp4?spm=5176.29623064.0.0.41ed26d6cBOhV3&file=092faf3c94244973ab752ee1280ba76f.mp4" # Replace with actual video URL + # Define video URL for testing + video_url = "https://cdn.wanx.aliyuncs.com/wanx/1719234057367822001/text_to_video/092faf3c94244973ab752ee1280ba76f.mp4?spm=5176.29623064.0.0.41ed26d6cBOhV3&file=092faf3c94244973ab752ee1280ba76f.mp4" print("\nTest: Adding video track") video_result = add_video_impl( @@ -2148,13 +2294,13 @@ if __name__ == "__main__": # test_audio02() # test_audio03() # test_audio04() - test_image01() + # test_image01() # test_image02() # test_image03() # test_image04() # test_video() # test_video_02() - # test_text() + test_text() # test_video_track01() # test_video_track02() # test_video_track03() @@ -2173,4 +2319,4 @@ if __name__ == "__main__": # test_generate_image02() # test_speech_01() # test_mask_01() - # test_mask_02() \ No newline at end of file + # test_mask_02() From c43f32d5a215a634acacaaa4ae0910231962a6e4 Mon Sep 17 00:00:00 2001 From: AshReo <33994521+ashreo@users.noreply.github.com> Date: Fri, 1 Aug 2025 10:42:14 +0800 Subject: [PATCH 2/4] Enhanced text features: multi-style, shadow, and background support Enhanced text features: multi-style, shadow, and background support - Added text multi-style support with TextStyleRange - Implemented text shadow effects with configurable parameters - Added text background with various style options - Updated example.py with comprehensive test cases - Fixed import issues in add_text_impl.py --- capcut_server.py | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/capcut_server.py b/capcut_server.py index d1caa97..79c475a 100644 --- a/capcut_server.py +++ b/capcut_server.py @@ -29,7 +29,8 @@ from save_draft_impl import save_draft_impl, query_task_status, query_script_imp from add_effect_impl import add_effect_impl from add_sticker_impl import add_sticker_impl from create_draft import create_draft -from util import generate_draft_url as utilgenerate_draft_url +from util import generate_draft_url as utilgenerate_draft_url, hex_to_rgb +from pyJianYingDraft.text_segment import TextStyleRange, Text_style, Text_border from settings.local import IS_CAPCUT_ENV, DRAFT_DOMAIN, PREVIEW_ROUTER, PORT @@ -325,14 +326,13 @@ def add_text(): transform_y = data.get('transform_y', 0) transform_x = data.get('transform_x', 0) font = data.get('font', "文轩体") - font_color = data.get('font_color', "#FF0000") - font_size = data.get('font_size', 8.0) + font_color = data.get('color', data.get('font_color', "#FF0000")) # Support both 'color' and 'font_color' + font_size = data.get('size', data.get('font_size', 8.0)) # Support both 'size' and 'font_size' track_name = data.get('track_name', "text_main") vertical = data.get('vertical', False) - font_alpha = data.get('font_alpha', 1.0) + font_alpha = data.get('alpha', data.get('font_alpha', 1.0)) # Support both 'alpha' and 'font_alpha' outro_animation = data.get('outro_animation', None) outro_duration = data.get('outro_duration', 0.5) - track_name = data.get('track_name', 'text_main') width = data.get('width', 1080) height = data.get('height', 1920) @@ -350,18 +350,18 @@ def add_text(): background_style = data.get('background_style', 0) background_alpha = data.get('background_alpha', 0.0) background_round_radius = data.get('background_round_radius', 0.0) - background_height = data.get('background_height', 0.14) # 背景高度,范围0.0-1.0 - background_width = data.get('background_width', 0.14) # 背景宽度,范围0.0-1.0 - background_horizontal_offset = data.get('background_horizontal_offset', 0.5) # 背景水平偏移,范围0.0-1.0 - background_vertical_offset = data.get('background_vertical_offset', 0.5) # 背景垂直偏移,范围0.0-1.0 + background_height = data.get('background_height', 0.14) # Background height, range 0.0-1.0 + background_width = data.get('background_width', 0.14) # Background width, range 0.0-1.0 + background_horizontal_offset = data.get('background_horizontal_offset', 0.5) # Background horizontal offset, range 0.0-1.0 + background_vertical_offset = data.get('background_vertical_offset', 0.5) # Background vertical offset, range 0.0-1.0 - # 阴影参数 - shadow_enabled = data.get('shadow_enabled', False) # 是否启用阴影 - shadow_alpha = data.get('shadow_alpha', 0.9) # 阴影透明度,范围0.0-1.0 - shadow_angle = data.get('shadow_angle', -45.0) # 阴影角度,范围-180.0-180.0 - shadow_color = data.get('shadow_color', "#000000") # 阴影颜色 - shadow_distance = data.get('shadow_distance', 5.0) # 阴影距离 - shadow_smoothing = data.get('shadow_smoothing', 0.15) # 阴影平滑度,范围0.0-1.0 + # Shadow parameters + shadow_enabled = data.get('shadow_enabled', False) # Whether to enable shadow + shadow_alpha = data.get('shadow_alpha', 0.9) # Shadow transparency, range 0.0-1.0 + shadow_angle = data.get('shadow_angle', -45.0) # Shadow angle, range -180.0-180.0 + shadow_color = data.get('shadow_color', "#000000") # Shadow color + shadow_distance = data.get('shadow_distance', 5.0) # Shadow distance + shadow_smoothing = data.get('shadow_smoothing', 0.15) # Shadow smoothing, range 0.0-1.0 # Bubble and decorative text effects bubble_effect_id = data.get('bubble_effect_id') @@ -376,17 +376,17 @@ def add_text(): outro_animation = data.get('outro_animation') outro_duration = data.get('outro_duration', 0.5) - # 新增多样式文本参数 + # Multi-style text parameters text_styles_data = data.get('text_styles', []) text_styles = None if text_styles_data: text_styles = [] for style_data in text_styles_data: - # 获取样式范围 + # Get style range start_pos = style_data.get('start', 0) end_pos = style_data.get('end', 0) - # 创建文本样式 + # Create text style style = Text_style( size=style_data.get('style',{}).get('size', font_size), bold=style_data.get('style',{}).get('bold', False), @@ -400,7 +400,7 @@ def add_text(): line_spacing=style_data.get('style',{}).get('line_spacing', 0) ) - # 创建描边(如果有) + # Create border (if any) border = None if style_data.get('border',{}).get('width', 0) > 0: border = Text_border( @@ -409,7 +409,7 @@ def add_text(): width=style_data.get('border',{}).get('width', border_width) ) - # 创建样式范围对象 + # Create style range object style_range = TextStyleRange( start=start_pos, end=end_pos, @@ -476,7 +476,7 @@ def add_text(): height=height, fixed_width=fixed_width, fixed_height=fixed_height, - text_styles = text_styles + text_styles=text_styles ) result["success"] = True @@ -1430,4 +1430,4 @@ def get_video_character_effect_types(): if __name__ == '__main__': - app.run(host='0.0.0.0', port=PORT) \ No newline at end of file + app.run(host='0.0.0.0', port=PORT) From 941f273e73aa7e2b1db948395cfb078fcbdcdb1b Mon Sep 17 00:00:00 2001 From: AshReo <33994521+ashreo@users.noreply.github.com> Date: Fri, 1 Aug 2025 10:42:58 +0800 Subject: [PATCH 3/4] Enhanced text features: multi-style, shadow, and background support Enhanced text features: multi-style, shadow, and background support - Added text multi-style support with TextStyleRange - Implemented text shadow effects with configurable parameters - Added text background with various style options - Updated example.py with comprehensive test cases - Fixed import issues in add_text_impl.py --- add_text_impl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/add_text_impl.py b/add_text_impl.py index a5226fe..29f8e1a 100644 --- a/add_text_impl.py +++ b/add_text_impl.py @@ -2,7 +2,7 @@ import pyJianYingDraft as draft from settings.local import IS_CAPCUT_ENV from util import generate_draft_url, hex_to_rgb from pyJianYingDraft import trange, Font_type -from typing import Optional +from typing import Optional, List # add List type hint from pyJianYingDraft import exceptions from create_draft import get_or_create_draft from pyJianYingDraft.text_segment import TextBubble, TextEffect, TextStyleRange From 477db0909a94989f31c2571a62ce9038b17954e0 Mon Sep 17 00:00:00 2001 From: AshReo <33994521+ashreo@users.noreply.github.com> Date: Fri, 1 Aug 2025 10:44:12 +0800 Subject: [PATCH 4/4] Create LICENSE --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.