외부에 링크된 이미지를 사용하여 프레젠테이션을 HTML로 내보내기
개요
기본적으로 Aspose.Slides는 프레젠테이션을 단일 HTML 파일로 내보냅니다. 이미지 및 기타 리소스는 일반적으로 Base64 데이터로 HTML에 직접 기록됩니다. 하나의 휴대 가능한 파일이 필요할 때는 편리하지만, 웹사이트, CMS 또는 서버 측 변환 파이프라인에 항상 최적의 형식은 아닙니다.
외부 링크 리소스를 사용하려는 경우:
- HTML 문서의 크기를 줄이기;
- 브라우저나 CDN에서 이미지, 폰트, 오디오, 비디오를 별도로 캐시하기;
- 내보낸 후 생성된 리소스를 검사, 교체, 압축 또는 후처리하기;
- 웹 애플리케이션이 기대하는 출력 구조에 가깝게 유지하기.
일반적인 HTML 변환 워크플로우에 대해서는 Convert PowerPoint Presentations to HTML를 참조하십시오. 이 문서는 내보내기의 리소스 연결 부분에 중점을 둡니다.
링크된 리소스 내보내기 작동 방식
ILinkEmbedController는 애플리케이션이 리소스별로 데이터를 HTML에 삽입할지 외부에 저장하고 링크를 쓸지 결정하도록 합니다.
이 인터페이스에는 세 가지 메서드가 있습니다:
ILinkEmbedController.getObjectStoringLocation은 리소스를 링크할지 임베드할지를 결정합니다.ILinkEmbedController.getUrl은 생성된 HTML 또는 다른 링크된 리소스에 기록될 URL을 반환합니다.ILinkEmbedController.saveExternal은 링크된 리소스 데이터를 디스크 또는 다른 저장 대상에 씁니다.
파일 시스템 경로와 브라우저 URL은 별개의 문제입니다. 예를 들어, 아래 샘플은 리소스 파일을 디스크의 html-output/assets에 기록하고, HTML에는 assets/resource-1.svg와 같은 상대 URL을 포함합니다. 브라우저는 링크가 포함된 파일을 기준으로 해당 URL을 해석합니다. 따라서 presentation.html에서 SVG 파일로의 링크는 assets/resource-1.svg를 사용하고, 그 SVG 파일이 같은 assets 폴더에 저장된 이미지로 연결될 때는 resource-4.jpg를 사용합니다.
링크된 리소스로 HTML 내보내기
다음 Python 예제는 출력 디렉터리를 생성하고 HTML 파일을 그곳에 저장하며, 링크된 리소스를 assets 하위 디렉터리에 저장합니다. 컨트롤러는 Aspose.Slides가 제공하거나 안전한 파일 확장자를 추론할 수 있는 경우 일반 이미지, 폰트, 오디오, 비디오 및 CSS 리소스를 링크합니다. 인식되지 않은 리소스는 그대로 임베드됩니다.
import jpype
import asposeslides
from pathlib import Path
if not jpype.isJVMStarted():
jpype.startJVM()
from asposeslides.api import HtmlFormatter, HtmlOptions, LinkEmbedDecision, Presentation, SVGOptions, SaveFormat, SlideImageFormat
class ExternalResourceController:
EXTENSIONS_BY_CONTENT_TYPE = {
"image/jpeg": ".jpg",
"image/png": ".png",
"image/gif": ".gif",
"image/bmp": ".bmp",
"image/svg+xml": ".svg",
"image/tiff": ".tiff",
"image/x-emf": ".emf",
"image/x-wmf": ".wmf",
"font/woff": ".woff",
"font/woff2": ".woff2",
"font/ttf": ".ttf",
"application/font-woff": ".woff",
"application/vnd.ms-fontobject": ".eot",
"application/x-font-ttf": ".ttf",
"text/css": ".css",
"audio/mpeg": ".mp3",
"audio/mp4": ".m4a",
"audio/wav": ".wav",
"video/mp4": ".mp4",
"video/webm": ".webm",
}
def __init__(self, asset_directory, asset_url_prefix):
self.asset_directory = asset_directory
normalized_prefix = asset_url_prefix.replace("\\", "/") if asset_url_prefix else ""
self.asset_url_prefix = normalized_prefix.rstrip("/") + "/" if normalized_prefix else ""
self.file_names_by_resource_id = {}
def getObjectStoringLocation(self, resource_id, entity_data, semantic_name, content_type, recommended_extension):
extension = self.resolve_extension(content_type, recommended_extension)
if extension is None:
return LinkEmbedDecision.Embed
self.file_names_by_resource_id[resource_id] = f"resource-{resource_id}{extension}"
return LinkEmbedDecision.Link
def getUrl(self, resource_id, referrer):
file_name = self.file_names_by_resource_id.get(resource_id)
if file_name is None:
return None
if referrer in self.file_names_by_resource_id:
return file_name
return self.asset_url_prefix + file_name
def saveExternal(self, resource_id, entity_data):
file_name = self.file_names_by_resource_id.get(resource_id)
if file_name is None:
print(f"Resource {resource_id} was not registered for external storage.")
return
if entity_data is None or len(entity_data) == 0:
print(f"Resource {resource_id} contains no data and cannot be saved.")
return
try:
self.asset_directory.mkdir(parents=True, exist_ok=True)
file_path = self.asset_directory / file_name
resource_data = bytes(entity_data)
file_path.write_bytes(resource_data)
except OSError as error:
print(f"Failed to save external resource {resource_id}: {error}")
@classmethod
def resolve_extension(cls, content_type, recommended_extension):
content_type = str(content_type) if content_type is not None else ""
mapped_extension = cls.EXTENSIONS_BY_CONTENT_TYPE.get(content_type)
if mapped_extension is not None:
return mapped_extension
if not content_type.lower().startswith(("image/", "font/", "audio/", "video/")):
return None
if recommended_extension is None:
return None
extension_characters = str(recommended_extension).strip().lstrip(".")
if not extension_characters or not extension_characters.isalnum():
return None
return "." + extension_characters.lower()
input_file_path = Path("presentation.pptx")
output_directory = Path("html-output")
asset_directory_name = "assets"
asset_directory = output_directory / asset_directory_name
output_directory.mkdir(parents=True, exist_ok=True)
asset_directory.mkdir(parents=True, exist_ok=True)
asset_url_prefix = asset_directory_name + "/"
controller = ExternalResourceController(asset_directory, asset_url_prefix)
controller_proxy = jpype.JProxy("com.aspose.slides.ILinkEmbedController", inst=controller)
svg_options = SVGOptions(controller_proxy)
slide_image_format = SlideImageFormat.svg(svg_options)
html_options = HtmlOptions(controller_proxy)
html_formatter = HtmlFormatter.createDocumentFormatter("", False)
html_options.setHtmlFormatter(html_formatter)
html_options.setSlideImageFormat(slide_image_format)
presentation = Presentation(str(input_file_path))
try:
html_file_path = output_directory / "presentation.html"
presentation.save(str(html_file_path), SaveFormat.Html, html_options)
finally:
presentation.dispose()
내보낸 후 출력 폴더는 다음과 같은 구조를 가집니다:
html-output/
presentation.html
assets/
resource-1.svg
resource-2.svg
resource-3.svg
resource-4.jpg
resource-5.png
정확한 파일은 프레젠테이션 내용 및 내보내기 옵션에 따라 달라집니다. 예를 들어 래스터 이미지는 일반적으로 JPEG 또는 PNG로 내보내집니다. Aspose.Slides는 파일 크기나 적합성을 고려해 원본 프레젠테이션과 다른 이미지 코덱을 선택할 수 있습니다. 투명도가 있는 이미지는 PNG로 내보내집니다.
배포용 URL 선택
샘플은 상대 URL 접두사 assets/를 사용합니다. presentation.html이 html-output/presentation.html에서 열리면 브라우저는 html-output/assets/resource-1.svg를 로드합니다.
하나의 링크된 리소스가 다른 링크된 리소스를 참조할 때, 샘플은 ILinkEmbedController.getUrl의 referrer 매개변수를 사용하고 파일 이름만 반환합니다. 예를 들어 resource-1.svg와 resource-4.jpg가 모두 assets 폴더에 있을 경우, SVG 파일은 resource-4.jpg를 참조해야 하며 assets/resource-4.jpg를 참조하지 않아야 합니다.
파일이 다른 위치에 배포될 경우 다른 URL 접두사를 사용하십시오:
- HTML 파일 옆에 자산 디렉터리가 있을 때
assets/사용. - 자산 디렉터리가 HTML 파일보다 한 단계 위에 있을 때
../assets/사용. - 파일이 CDN이나 정적 파일 서버에 업로드될 때
https://cdn.example.com/presentations/job-123/assets/사용.
ILinkEmbedController.getUrl이 반환하는 URL은 ILinkEmbedController.saveExternal이 작성한 파일의 최종 배포 위치와 일치해야 합니다. 서버 애플리케이션에서는 각 변환 작업마다 고유한 출력 디렉터리 또는 객체 저장소 접두사를 사용하여 다른 내보내기의 파일이 덮어쓰이는 것을 방지하십시오.
대신 임베드해야 할 경우
임베드된 Base64 HTML은 출력이 하나의 파일이어야 할 때 여전히 유용합니다(예: 이메일 첨부 파일, 오프라인 미리보기, 별도의 자산 폴더 없이 이동되는 문서). 링크된 리소스는 HTML이 웹 애플리케이션을 통해 제공되거나 CMS에 저장되거나 빌드 파이프라인에서 최적화되거나 브라우저가 HTML과 독립적으로 캐시할 때 더 적합합니다.
FAQ
이미지만 외부화하고 다른 리소스는 임베드된 상태로 유지할 수 있나요?
예. ILinkEmbedController.getObjectStoringLocation에서 별도 파일로 저장하고 싶은 콘텐츠 유형에 대해서만 LinkEmbedDecision.Link을 반환하고, 나머지는 LinkEmbedDecision.Embed으로 반환합니다.
내보낸 이미지 확장자가 원본 프레젠테이션과 다른 이유는 무엇인가요?
Aspose.Slides는 HTML 내보내기 중 래스터 이미지를 재인코딩하여 크기나 브라우저 호환성을 개선할 수 있습니다. 예를 들어 원본 파일의 이미지는 렌더링 결과에 따라 JPEG 또는 PNG로 기록될 수 있습니다.
HTML 파일을 이동한 후에도 상대 URL이 작동하나요?
상대 URL은 동일한 상대 폴더 구조가 유지될 때만 작동합니다. HTML이 assets/resource-1.png를 참조한다면 assets 폴더는 HTML 파일 옆에 있어야 하며, 다른 URL 접두사를 생성하지 않은 경우에는 이동 시 함께 옮겨야 합니다.
서버 애플리케이션이 동일한 출력 폴더를 재사용해도 되나요?
아니요. 각 변환 작업마다 고유한 출력 디렉터리 또는 저장 접두사를 사용하십시오. 이렇게 하면 파일 이름 충돌을 방지하고 한 내보내기가 다른 내보내기의 리소스를 덮어쓰는 일을 방지할 수 있습니다.