feat: add support for exporting pdf and png (#2588)

This closes #2583.
This commit is contained in:
fourdim
2023-05-30 14:04:35 +08:00
committed by GitHub
parent 4175f5391e
commit fa089de40d
12 changed files with 257 additions and 2 deletions

View File

@@ -0,0 +1,32 @@
import { style } from '@vanilla-extract/css';
export const waterMarkStyle = style({
display: 'none',
'@media': {
print: {
position: 'absolute',
bottom: '0',
right: '20px',
zIndex: 100,
display: 'block',
width: 'auto',
filter: 'opacity(20%)',
},
},
});
export const linkStyle = style({
textAlign: 'left',
color: 'black',
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-end',
alignItems: 'center',
width: '200px',
});
export const linkTextStyle = style({
whiteSpace: 'nowrap',
});
export const iconStyle = style({
fontSize: '20px',
});

View File

@@ -0,0 +1,19 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import clsx from 'clsx';
import { linkStyle, linkTextStyle, waterMarkStyle } from './index.css';
import { AffineLogoIcon } from './logo';
export const AffineWatermark = () => {
const t = useAFFiNEI18N();
return (
<div className={clsx()}>
<div data-testid="affine-watermark" className={clsx(waterMarkStyle)}>
<a className={linkStyle} href="https://affine.pro">
<div className={linkTextStyle}>{t['Created with']()}</div>
<AffineLogoIcon />
</a>
</div>
</div>
);
};

View File

@@ -0,0 +1,22 @@
export const AffineLogoIcon = () => {
return (
<svg
id="Layer_2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1459.61 470"
>
<g id="_6_export">
<path
id="logo-black"
d="m404.14,350.34c-5.95-10.33-15.86-27.48-25.65-44.42-2.97-5.14-5.92-10.25-8.75-15.15-5.82-10.08-11.12-19.27-14.9-25.83-26.23-45.33-77.87-135.08-103.67-179.46-7.95-12.37-26.37-11.39-33.21,1.42-7.76,13.45-16.3,28.24-25.32,43.87-2.86,4.96-5.78,10.01-8.73,15.12-37.58,65.08-81.56,141.26-112.89,195.52-1.6,2.9-4.58,7.63-6.07,10.76-2.61,5.65-2.1,12.69,1.19,17.91,3.71,6.18,10.65,9.57,17.74,9.22,8.53,0,26.62-.01,50.01,0,5.55,0,11.4,0,17.49,0,81.33,0,205.57.05,236.06,0,14.81.03,24.1-16.21,16.72-28.97Zm-175.07-57.64l-14.97-25.93c-2.63-4.56.66-10.26,5.92-10.26h29.94c5.27,0,8.56,5.7,5.92,10.26l-14.97,25.93c-2.63,4.56-9.21,4.56-11.85,0Zm-25.08-48.29c-1.24-3.16-2.31-6.37-3.19-9.63l49.53,9.63h-46.34Zm22.62,68.22c-2.11,2.66-4.36,5.19-6.74,7.59l-16.43-47.71,23.16,40.12Zm47.78-53.7c3.35.5,6.67,1.19,9.93,2.05l-33.1,38.08,23.17-40.13Zm-76.64-40.4c-.53-4.82-.76-9.69-.74-14.57l65.41,31.91-64.68-17.33Zm-7.76,47.77l17.32,64.65c-3.91,2.87-8.01,5.51-12.25,7.93l-5.08-72.58Zm109.93.17c4.44,1.95,8.77,4.19,12.99,6.65l-60.34,40.7,47.35-47.35Zm-101.41-83.09c1.2-8.86,3.05-17.59,5.29-25.96l99.37,86.38-104.65-60.42Zm-22.02,164.51c-8.27,3.39-16.75,6.15-25.12,8.39l25.12-129.23v120.84Zm153.49-63.19c7.07,5.47,13.71,11.44,19.84,17.56l-124.49,42.86,104.65-60.42Zm-90.64-175.85c18.32,31.79,44.92,77.89,70.26,121.77l-94.61-94.61c5.56-9.62,10.83-18.75,15.69-27.18,1.93-3.33,6.73-3.33,8.66,0Zm-147.77,240.92c5.21-8.99,12.37-21.32,13.96-24.16,15.08-26.12,35.39-61.29,56.37-97.63l-34.65,129.3c-12.41,0-23.11,0-31.35,0-3.85,0-6.26-4.17-4.33-7.5Zm282.54,7.53c-28.89,0-84.98,0-140.67,0l129.31-34.65c6.78,11.74,12.21,21.14,15.69,27.16,1.93,3.33-.48,7.49-4.32,7.49Z"
/>
<rect fill="none" width="1459.61" height="470" />
<path d="m594.24,116.6c-1.17-4.72-5.41-8.03-10.27-8.03h-16.41c-4.86,0-9.1,3.31-10.27,8.03l-55.64,224.39c-1.65,6.67,3.39,13.12,10.27,13.12h5.66c4.92,0,9.19-3.39,10.3-8.18l11.85-50.91c1.11-4.79,5.38-8.18,10.3-8.18h51.46c4.92,0,9.19,3.39,10.3,8.18l11.85,50.91c1.11,4.79,5.38,8.18,10.3,8.18h5.66c6.87,0,11.92-6.45,10.27-13.12l-55.64-224.39Zm-3.15,146.69h-30.65c-6.81,0-11.85-6.34-10.3-12.97l20.48-98.57c1.27-5.45,9.03-5.45,10.3,0l20.48,98.57c1.54,6.63-3.49,12.97-10.3,12.97Z" />
<path d="m994.58,215.86h-126.97c-5.84,0-10.58-4.74-10.58-10.58v-53.08c0-11.1,9-20.1,20.1-20.1h62.83c5.84,0,10.58-4.74,10.58-10.58v-2.39c0-5.84-4.74-10.58-10.58-10.58h-70.32c-22.2,0-40.19,18-40.19,40.19v67.1h-113.04c-5.84,0-10.58-4.74-10.58-10.58v-53.08c0-11.1,9-20.1,20.1-20.1h62.83c5.84,0,10.58-4.74,10.58-10.58v-2.39c0-5.84-4.74-10.58-10.58-10.58h-70.32c-22.2,0-40.19,18-40.19,40.19v194.77c0,5.84,4.74,10.58,10.58,10.58h6.43c5.84,0,10.58-4.74,10.58-10.58v-93.54c0-5.84,4.74-10.58,10.58-10.58h113.04v104.12c0,5.84,4.74,10.58,10.58,10.58h6.43c5.84,0,10.58-4.74,10.58-10.58v-93.54c0-5.84,4.74-10.58,10.58-10.58h120.16c11.1,0,20.1,9,20.1,20.1v84.14c0,5.84,4.74,10.58,10.58,10.58h5.75c5.84,0,10.58-4.74,10.58-10.58v-87.59c0-22.2-18-40.19-40.19-40.19Z" />
<path d="m1202.87,108.57h-5.79c-5.83,0-10.56,4.72-10.58,10.54l-.64,201.36-57.7-204.2c-1.29-4.56-5.44-7.7-10.18-7.7h-25.15c-5.84,0-10.58,4.74-10.58,10.58v224.39c0,5.84,4.74,10.58,10.58,10.58h5.79c5.83,0,10.56-4.72,10.58-10.54l.64-201.36,57.7,204.2c1.29,4.56,5.44,7.7,10.18,7.7h25.15c5.84,0,10.58-4.74,10.58-10.58V119.14c0-5.84-4.74-10.58-10.58-10.58Z" />
<path d="m1293.92,132.11h59.81c5.84,0,10.58-4.74,10.58-10.58v-2.39c0-5.84-4.74-10.58-10.58-10.58h-66.95c-22.2,0-40.19,18-40.19,40.19v165.15c0,22.2,18,40.19,40.19,40.19h66.95c5.84,0,10.58-4.74,10.58-10.58v-2.39c0-5.84-4.74-10.58-10.58-10.58h-59.81c-11.1,0-20.1-9-20.1-20.1v-63.84c0-5.84,4.74-10.58,10.58-10.58h65.96c5.84,0,10.58-4.74,10.58-10.58v-2.39c0-5.84-4.74-10.58-10.58-10.58h-65.96c-5.84,0-10.58-4.74-10.58-10.58v-49.72c0-11.1,9-20.1,20.1-20.1Z" />
<path d="m1018.67,138.5c-4.7-4.7-12.71-2.55-14.43,3.87l-7.77,29.01c-1.72,6.41,4.15,12.28,10.57,10.57l29.01-7.77c6.42-1.72,8.56-9.74,3.87-14.43l-21.23-21.23Z" />
</g>
</svg>
);
};

View File

@@ -1,11 +1,14 @@
import { Menu, MenuItem } from '@affine/component';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { PageBlockModel } from '@blocksuite/blocks';
import { ContentParser } from '@blocksuite/blocks/content-parser';
import {
ArrowRightSmallIcon,
ExportIcon,
ExportToHtmlIcon,
ExportToMarkdownIcon,
ExportToPdfIcon,
ExportToPngIcon,
} from '@blocksuite/icons';
import { useRef } from 'react';
@@ -14,7 +17,7 @@ import type { CommonMenuItemProps } from './types';
export const Export = ({
onSelect,
onItemClick,
}: CommonMenuItemProps<{ type: 'markdown' | 'html' }>) => {
}: CommonMenuItemProps<{ type: 'markdown' | 'html' | 'pdf' | 'png' }>) => {
const t = useAFFiNEI18N();
const contentParserRef = useRef<ContentParser>();
return (
@@ -24,6 +27,29 @@ export const Export = ({
trigger="click"
content={
<>
<MenuItem
data-testid="export-to-pdf"
onClick={async () => {
if (!contentParserRef.current) {
contentParserRef.current = new ContentParser(
globalThis.currentEditor!.page
);
}
const result = await window.apis?.export.savePDFFileAs(
(
globalThis.currentEditor!.page.root as PageBlockModel
).title.toString()
);
if (result !== undefined) {
return;
}
contentParserRef.current.exportPdf();
onSelect?.({ type: 'pdf' });
}}
icon={<ExportToPdfIcon />}
>
{t['Export to PDF']()}
</MenuItem>
<MenuItem
data-testid="export-to-html"
onClick={() => {
@@ -39,6 +65,21 @@ export const Export = ({
>
{t['Export to HTML']()}
</MenuItem>
<MenuItem
data-testid="export-to-png"
onClick={() => {
if (!contentParserRef.current) {
contentParserRef.current = new ContentParser(
globalThis.currentEditor!.page
);
}
contentParserRef.current.exportPng();
onSelect?.({ type: 'png' });
}}
icon={<ExportToPngIcon />}
>
{t['Export to PNG']()}
</MenuItem>
<MenuItem
data-testid="export-to-markdown"
onClick={() => {