55using SupernoteSharp . Entities ;
66using System ;
77using System . Collections . Generic ;
8+ using System . IO ;
9+ using System . Linq ;
10+ using System . Runtime . CompilerServices ;
811using System . Text . Json ;
12+ using VectSharp ;
13+ using VectSharp . PDF ;
14+ using Page = SupernoteSharp . Entities . Page ;
915
1016namespace SupernoteSharp . Business
1117{
@@ -212,5 +218,147 @@ private Image<Rgba32> FlattenImage(Image<Rgba32> foreground, Image<Rgba32> backg
212218 return background ;
213219 }
214220 }
221+
222+ public class PdfConverter
223+ {
224+ private const int ALL_PAGES = - 1 ;
225+
226+ private Notebook _notebook ;
227+ private ColorPalette _palette ;
228+
229+ public PdfConverter ( Notebook notebook , ColorPalette palette )
230+ {
231+ _notebook = notebook ;
232+ _palette = palette ;
233+ }
234+
235+ public byte [ ] Convert ( int pageNumber , bool vectorize = false , bool enableLinks = false )
236+ {
237+ Dictionary < int , Image > pageImages = new Dictionary < int , Image > ( ) ;
238+
239+ if ( vectorize == true )
240+ // TODO: Implement vectorized image
241+ throw new NotImplementedException ( ) ;
242+ else
243+ {
244+ ImageConverter converter = new Converter . ImageConverter ( _notebook , DefaultColorPalette . Grayscale ) ;
245+ if ( pageNumber == ALL_PAGES )
246+ {
247+ List < Image > images = converter . ConvertAll ( VisibilityOverlay . Default ) ;
248+ for ( int i = 0 ; i < images . Count ; i ++ )
249+ pageImages . Add ( i , images [ i ] ) ;
250+ }
251+ else
252+ pageImages . Add ( pageNumber , converter . Convert ( pageNumber , VisibilityOverlay . Default ) ) ;
253+ }
254+
255+ return CreatePdf ( pageImages , vectorize , enableLinks ) ;
256+ }
257+
258+ public byte [ ] ConvertAll ( bool vectorize = false , bool enableLinks = false )
259+ {
260+ return Convert ( ALL_PAGES , vectorize , enableLinks ) ;
261+ }
262+
263+ private byte [ ] CreatePdf ( Dictionary < int , Image > pageImages , bool vectorize , bool enableLinks )
264+ {
265+ Dictionary < int , VectSharp . Page > pdfPages = new Dictionary < int , VectSharp . Page > ( ) ;
266+
267+ // A4 page size is 11.01" x 15.58"
268+ // For a PDF document, each dot is 1/72nd of an inch
269+ double pageWidth = 210 * 72 / 25.4 ; // width in pixels
270+ double pageHeight = 297 * 72 / 25.4 ; // height in pixels
271+
272+ foreach ( KeyValuePair < int , Image > kvp in pageImages )
273+ {
274+ Image pageImage = kvp . Value ;
275+ VectSharp . Page pdfPage = new VectSharp . Page ( pageWidth , pageHeight ) ;
276+
277+ // set image scale to fit the pdf page
278+ pdfPage . Graphics . Scale ( pageWidth / pageImage . Width , pageHeight / pageImage . Height ) ;
279+
280+ // convert image to byte[]
281+ byte [ ] imageBytes = new byte [ pageImage . Width * pageImage . Height * Unsafe . SizeOf < Rgba32 > ( ) ] ;
282+ pageImage . CloneAs < Rgba32 > ( ) . CopyPixelDataTo ( imageBytes ) ;
283+
284+ // draw the image onto the pdf page
285+ pdfPage . Graphics . DrawRasterImage ( 0 , 0 , new RasterImage ( imageBytes , pageImage . Width , pageImage . Height , PixelFormats . RGBA , false ) ) ;
286+
287+ // add completed page
288+ pdfPages . Add ( kvp . Key , pdfPage ) ;
289+ }
290+
291+ // add links if requested
292+ Dictionary < string , string > links = null ;
293+ if ( enableLinks == true )
294+ links = AddLinks ( pdfPages ) ;
295+
296+ // create the final pdf document
297+ Document pdfDocument = new Document ( ) ;
298+ pdfDocument . Pages . AddRange ( pdfPages . Values ) ;
299+
300+ using ( MemoryStream memoryStream = new MemoryStream ( ) )
301+ {
302+ pdfDocument . SaveAsPDF ( memoryStream , linkDestinations : links ) ;
303+ return memoryStream . ToArray ( ) ;
304+ }
305+ }
306+
307+ private Dictionary < string , string > AddLinks ( Dictionary < int , VectSharp . Page > pdfPages )
308+ {
309+ List < Link > noteLinks = _notebook . Links ;
310+ Dictionary < string , string > links = new Dictionary < string , string > ( ) ;
311+
312+ foreach ( KeyValuePair < int , VectSharp . Page > kvp in pdfPages )
313+ {
314+ // get all outbound links for the current page
315+ List < Link > outboundLinks = noteLinks . Where ( x => x . PageNumber == kvp . Key && x . InOut == ( int ) LinkDirection . Out ) . ToList ( ) ;
316+ if ( outboundLinks . Count == 0 )
317+ continue ;
318+
319+ // link all web links
320+ List < Link > webLinks = outboundLinks . Where ( x => x . Type == ( int ) LinkType . Web ) . ToList ( ) ;
321+ foreach ( Link webLink in webLinks )
322+ {
323+ string webLinkTag = $ "WebLink_{ webLink . Metadata [ "LINKBITMAP" ] } ";
324+ string webLinkUrl = System . Text . Encoding . UTF8 . GetString ( System . Convert . FromBase64String ( webLink . FilePath ) ) ;
325+
326+ kvp . Value . Graphics . StrokeRectangle ( webLink . Rect . left , webLink . Rect . top , webLink . Rect . width , webLink . Rect . height ,
327+ Colour . FromRgba ( 0 , 0 , 0 , 0 ) , tag : webLinkTag ) ;
328+
329+ links . Add ( webLinkTag , webLinkUrl ) ;
330+ }
331+
332+ // if we only have one page, do not build the links between pages
333+ if ( pdfPages . Count == 1 )
334+ continue ;
335+
336+ // link all internal page links
337+ List < Link > sourceLinks = outboundLinks . Where ( x => x . Type == ( int ) LinkType . Page ) . ToList ( ) ;
338+ foreach ( Link sourceLink in sourceLinks )
339+ {
340+ bool isInternalLink = ( sourceLink . FileId == _notebook . FileId ) ;
341+ if ( isInternalLink == true )
342+ {
343+ // each internal link is a pair of outbound and inbound, they have the same timestamp and rect coordinates
344+ Link targetLink = noteLinks . Where ( x => x . InOut == ( int ) LinkDirection . In && x . Timestamp == sourceLink . Timestamp && x . Rect . Equals ( sourceLink . Rect ) ) . FirstOrDefault ( ) ;
345+
346+ string sourceLinkTag = $ "SourceLink_{ sourceLink . Metadata [ "LINKBITMAP" ] } ";
347+ string targetLinkTag = $ "TargetLink_{ targetLink . Metadata [ "LINKBITMAP" ] } ";
348+
349+ pdfPages [ sourceLink . PageNumber ] . Graphics . StrokeRectangle ( sourceLink . Rect . left , sourceLink . Rect . top , sourceLink . Rect . width , sourceLink . Rect . height ,
350+ Colour . FromRgba ( 0 , 0 , 0 , 0 ) , tag : sourceLinkTag ) ;
351+
352+ pdfPages [ targetLink . PageNumber ] . Graphics . StrokeRectangle ( targetLink . Rect . left , 0 , targetLink . Rect . width , targetLink . Rect . height ,
353+ Colour . FromRgba ( 0 , 0 , 0 , 0 ) , tag : targetLinkTag ) ;
354+
355+ links . Add ( sourceLinkTag , $ "#{ targetLinkTag } ") ;
356+ }
357+ }
358+ }
359+
360+ return links ;
361+ }
362+ }
215363 }
216364}
0 commit comments