1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
|
// Basic PDF viewer using MuPDF C# bindings.
//
public class MuPDFGui : System.Windows.Forms.Form
{
// We use static pixmap to ensure it isn't garbage-collected.
mupdf.FzPixmap pixmap;
private System.Windows.Forms.MainMenu menu;
private System.Windows.Forms.MenuItem menu_item_file;
/* Zooming works by incrementing self.zoom by +/- 1 then using
magnification = 2**(self.zoom/self.zoom_multiple). */
private int zoom_multiple = 4;
private double zoom = 0;
private int page_number = 0;
mupdf.FzDocument document;
mupdf.FzPage page;
System.Drawing.Bitmap bitmap;
System.Windows.Forms.PictureBox picture_box;
// Need STAThread here otherwise OpenFileDialog hangs.
[System.STAThread]
public static void Main()
{
System.Windows.Forms.Application.Run(new MuPDFGui());
}
public MuPDFGui()
{
menu_item_file = new System.Windows.Forms.MenuItem("File",
new System.Windows.Forms.MenuItem[]
{
new System.Windows.Forms.MenuItem("&Open...", new System.EventHandler(this.open)),
new System.Windows.Forms.MenuItem("&Show html", new System.EventHandler(this.show_html)),
new System.Windows.Forms.MenuItem("&Quit", new System.EventHandler(this.quit))
}
);
menu = new System.Windows.Forms.MainMenu(new System.Windows.Forms.MenuItem [] {menu_item_file});
this.Menu = menu;
Resize += handle_resize;
KeyDown += handle_key_down;
this.picture_box = new System.Windows.Forms.PictureBox();
this.picture_box.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.AutoScroll = true;
Controls.Add(picture_box);
this.open_file("zlib.3.pdf");
}
public void open(System.Object sender, System.EventArgs e)
{
var dialog = new System.Windows.Forms.OpenFileDialog();
var result = dialog.ShowDialog();
if (result == System.Windows.Forms.DialogResult.OK)
{
this.open_file(dialog.FileName);
}
}
void open_file(string path)
{
try
{
this.document = new mupdf.FzDocument(path);
}
catch (System.Exception e)
{
System.Console.WriteLine("Failed to open: " + path + " becase: " + e);
return;
}
this.goto_page(0, 0);
}
public void show_html(System.Object sender, System.EventArgs e)
{
System.Console.WriteLine("ShowHtml() called");
var buffer = this.page.fz_new_buffer_from_page_with_format(
"docx",
"html",
new mupdf.FzMatrix(1, 0, 0, 1, 0, 0),
new mupdf.FzCookie()
);
System.Console.WriteLine("buffer=" + buffer);
var html_bytes = buffer.fz_buffer_extract();
var html_string = System.Text.Encoding.UTF8.GetString(html_bytes, 0, html_bytes.Length);
var web_browser = new System.Windows.Forms.WebBrowser();
web_browser.DocumentText = html_string;
web_browser.Show();
}
public void quit(System.Object sender, System.EventArgs e)
{
System.Console.WriteLine("Quit() called");
System.Windows.Forms.Application.Exit();
}
// Shows page. If width and/or height are zero we use .Width and/or .Height.
//
// To preserve current page and/or zoom, use .page_number and/or .zoom.
//
public void goto_page(int page_number, double zoom)
{
if (page_number < 0 || page_number >= document.fz_count_pages())
{
return;
}
this.zoom = zoom;
this.page_number = page_number;
this.page = document.fz_load_page(page_number);
var z = System.Math.Pow(2, this.zoom / this.zoom_multiple);
/* For now we always use 'fit width' view semantics. */
var page_rect = this.page.fz_bound_page();
var vscroll_width = System.Windows.Forms.SystemInformation.VerticalScrollBarWidth;
z *= (this.ClientSize.Width - vscroll_width) / (page_rect.x1 - page_rect.x0);
if (System.Type.GetType("Mono.Runtime") != null)
{
/* Use pixmap data without copying. This does not work on
Windows.
It looks like it's important to use MuPDF Fixed_RGB with
alpha=1, and C#'s Format32bppRgb. Other combinations,
e.g. (Fixed_RGB with alpha=0) and Format24bppRgb, result in a
blank display. */
var stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Reset();
stopwatch.Start();
this.pixmap = this.page.fz_new_pixmap_from_page_contents(
new mupdf.FzMatrix((float) z, 0, 0, (float) z, 0, 0),
new mupdf.FzColorspace(mupdf.FzColorspace.Fixed.Fixed_RGB),
1 /*alpha*/
);
stopwatch.Stop();
var t_pixmap = stopwatch.Elapsed;
stopwatch.Reset();
stopwatch.Start();
this.bitmap = new System.Drawing.Bitmap(
this.pixmap.fz_pixmap_width(),
this.pixmap.fz_pixmap_height(),
this.pixmap.fz_pixmap_stride(),
System.Drawing.Imaging.PixelFormat.Format32bppRgb,
(System.IntPtr) this.pixmap.fz_pixmap_samples_int()
);
stopwatch.Stop();
var t_bitmap = stopwatch.Elapsed;
stopwatch.Reset();
stopwatch.Start();
// This is slow for large pixmaps/bitmaps.
//check(pixmap, bitmap, 4);
stopwatch.Stop();
var t_check = stopwatch.Elapsed;
/*System.Console.WriteLine(""
+ " t_pixmap=" + t_pixmap
+ " t_bitmap=" + t_bitmap
+ " t_check=" + t_check
);*/
}
else
{
/* Copy pixmap's pixels into bitmap. This works on both Linux
(Mono) and Windows.
Unlike above, it seems that we need to use MuPDF Fixed_RGB with
alpha=0, and C#'s Format32bppRgb. Other combinations give a
blank display (possibly with alpha=0 for each pixel). */
this.pixmap = this.page.fz_new_pixmap_from_page_contents(
new mupdf.FzMatrix((float) z, 0, 0, (float) z, 0, 0),
new mupdf.FzColorspace(mupdf.FzColorspace.Fixed.Fixed_RGB),
0 /*alpha*/
);
this.bitmap = new System.Drawing.Bitmap(
this.pixmap.fz_pixmap_width(),
this.pixmap.fz_pixmap_height(),
System.Drawing.Imaging.PixelFormat.Format32bppRgb
);
long samples = pixmap.fz_pixmap_samples_int();
int stride = pixmap.fz_pixmap_stride();
for (int x=0; x<bitmap.Width; x+=1)
{
for (int y=0; y<bitmap.Height; y+=1)
{
unsafe
{
byte* sample = (byte*) samples + stride * y + 3 * x;
var color = System.Drawing.Color.FromArgb(sample[0], sample[1], sample[2]);
this.bitmap.SetPixel( x, y, color);
}
}
}
//check(pixmap, bitmap, 3);
}
this.picture_box.Image = this.bitmap;
}
private void handle_key_down(object sender, System.Windows.Forms.KeyEventArgs e)
{
//System.Console.WriteLine("HandleKeyDown: " + e.KeyCode);
if (e.Shift && e.KeyCode == System.Windows.Forms.Keys.PageUp)
{
goto_page(this.page_number - 1, this.zoom);
}
else if (e.Shift && e.KeyCode == System.Windows.Forms.Keys.PageDown)
{
goto_page(this.page_number + 1, this.zoom);
}
else if (e.KeyCode == System.Windows.Forms.Keys.D0)
{
goto_page(this.page_number, 0);
}
else if (e.KeyCode == System.Windows.Forms.Keys.Add
|| e.KeyCode == System.Windows.Forms.Keys.Oemplus
)
{
goto_page(this.page_number, this.zoom + 1);
}
else if (e.KeyCode == System.Windows.Forms.Keys.Subtract
|| e.KeyCode == System.Windows.Forms.Keys.OemMinus)
{
goto_page(this.page_number, this.zoom - 1);
}
}
private void handle_resize(object sender, System.EventArgs e)
{
goto_page(page_number, zoom);
}
// Throws exception if pixmap and bitmap differ.
void check(mupdf.FzPixmap pixmap, System.Drawing.Bitmap bitmap, int pixmap_bytes_per_pixel)
{
long samples = pixmap.fz_pixmap_samples_int();
if (pixmap.fz_pixmap_width() != bitmap.Width || pixmap.fz_pixmap_height() != bitmap.Height)
{
throw new System.Exception("Inconsistent sizes:"
+ " pixmap=(" + pixmap.fz_pixmap_width() + " " + pixmap.fz_pixmap_height()
+ " bitmap=(" + bitmap.Width + " " + bitmap.Height
);
}
int stride = pixmap.fz_pixmap_stride();
for (int x=0; x<bitmap.Width; x+=1)
{
for (int y=0; y<bitmap.Height; y+=1)
{
unsafe
{
byte* sample = (byte*) samples + stride * y + pixmap_bytes_per_pixel * x;
System.Drawing.Color color = bitmap.GetPixel( x, y);
if (color.R != sample[0] || color.G != sample[1] || color.B != sample[2])
{
string pixmap_pixel_text = "";
for (int i=0; i<pixmap_bytes_per_pixel; ++i)
{
if (i > 0) pixmap_pixel_text += " ";
pixmap_pixel_text += sample[i];
}
throw new System.Exception("Pixels differ: (" + x + " " + y + "):"
+ " pixmap: (" + pixmap_pixel_text + ")"
+ " bitmap: " + color);
}
}
}
}
}
}
|