Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 亚洲成年看片在线观看男男,亚洲不卡电影,亚洲精品国产福利

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          前端頁(yè)面如何原樣快速導(dǎo)出PDF?附示例

          何保持頁(yè)面樣式基本不變的前提下將HTML頁(yè)面導(dǎo)出為PDF,下面提供一些示例代碼,純屬個(gè)人原創(chuàng),如對(duì)你有幫助請(qǐng)記得加關(guān)注、加收藏、點(diǎn)贊、轉(zhuǎn)發(fā)、分享~謝謝~~

          • 基本思路:保持頁(yè)面樣式基本不變,使用 `html2canvas` 將頁(yè)面轉(zhuǎn)換為圖片,然后再通過(guò) `jspdf` 將圖片分頁(yè)導(dǎo)出為PDF文件(中間會(huì)遇到圖片或文字等內(nèi)容在分頁(yè)處被切割開的問(wèn)題,如何解決了?詳見末尾干貨)
          • 上基礎(chǔ)代碼:下面為項(xiàng)目中實(shí)際代碼截取
          <div>
              <!-- 要打印的內(nèi)容區(qū) -->
              <div ref="contentRef">
                  <div class="print-item print-out-flow">這是脫離文檔流的內(nèi)容區(qū)域</div>
                  <div class="print-item">這是一行內(nèi)容,也是最小葉子元素內(nèi)容</div>
              </div>
              <!-- 打印內(nèi)容容器 -->
              <div ref="printContainerRef" class="print-container"></div>
          </div>
          /**
            * 1.使用一個(gè)隱藏div裝載有滾動(dòng)條的div.innerHTML
            * 2.隱藏div使用position: absolute, z-index: -999, left: -9999px, width: 900px 控制讓用戶無(wú)感知
            * 3.根據(jù)需要覆寫隱藏div內(nèi)html樣式(例如textarea多行顯示有問(wèn)題, 可以新增一個(gè)隱藏的div
            *    包裹textarea的綁定值, 然后在打印樣式中覆寫樣式, 隱藏textarea并顯示對(duì)應(yīng)div)
            */
          handleExport() {
             // 下面是VUE組件內(nèi)獲取DOM元素代碼,將內(nèi)容放置到打印區(qū)(定義的隱藏DIV)中
              const contentRef = this.$refs.contentRef as HTMLElement;
              const printContainerRef = this.$refs.printContainerRef as HTMLElement;
              // 打印區(qū)的需額外處理絕對(duì)定位值, 調(diào)整使得第一個(gè)元素的.top值為0, 以便于頁(yè)面計(jì)算
              printContainerRef.innerHTML = contentRef.innerHTML;	
              
              // 所有葉子div元素加上 print-item 樣式名, 脫離文檔流的額外添加 print-out-flow
              handlePrintItem(printContainerRef);  // 解決多頁(yè)內(nèi)容可能被切割問(wèn)題
              
              html2canvas(printContainerRef, {allowTaint: false, useCORS: true}).then((canvas: any) => {
                const contentHeight = canvas.height;
                const contentWidth = canvas.width;
                // pdf每頁(yè)顯示的內(nèi)容高度
                const pageHeight = contentWidth / 595.28 * 841.89;
                // 未生成pdf的頁(yè)面高度
                let offsetHeight = contentHeight;
                // 頁(yè)面偏移值
                let position = 0;
                // a4紙的尺寸[595.28, 841.89], canvas圖片按a4紙大小縮放后的寬高
                const imgWidth = 595.28;
                const imgHeight = 595.28 / contentWidth * contentHeight;
          
                const dataURL = canvas.toDataURL('image/jpeg', 1.0);
                const doc = new jsPDF('p', 'pt', 'a4');
          
                if (offsetHeight < pageHeight) {
                  doc.addImage(dataURL, 'JPEG', 0, 0, imgWidth, imgHeight);
                } else {
                  while (offsetHeight > 0) {
                    doc.addImage(dataURL, 'JPEG', 0, position, imgWidth, imgHeight);
                    offsetHeight -= pageHeight;
                    position -= 841.89;
          
                    if (offsetHeight > 0) {
                      doc.addPage();
                    }
                  }
                }
          
                doc.save(this.generateReportFileName());
                printContainerRef.innerHTML = '';
              });
          }

          上干貨代碼:上面分頁(yè)導(dǎo)出PDF可能網(wǎng)上能看到類型代碼,但絕對(duì)找不到下面的代碼,純手搓解決分頁(yè)元素被切開問(wèn)題(思路:獲取自身定位,如自己剛好在被分頁(yè)處,則加上一定的margin-top值將內(nèi)容向下移)

          /** 
           * 處理打印元素項(xiàng), 修復(fù)分頁(yè)后被切割的元素
           * @param printContainerRef 打印內(nèi)容div容器
           * @param itemClassName 打印最小元素標(biāo)識(shí)類名
           * @param outFlowClassName 脫離文檔流的元素標(biāo)識(shí)類名
           */
          export function handlePrintItem(
            printContainerRef: HTMLElement,
            itemClassName: string = 'print-item',
            outFlowClassName: string = 'print-out-flow'
          ): void {
            const rootClientRect = printContainerRef.getBoundingClientRect();
            // 初始化頁(yè)面相關(guān)數(shù)據(jù)
            const totalHeight = rootClientRect.height;  // 內(nèi)容總高度
            const a4PageHeight = (printContainerRef.clientWidth / 595.28) * 841.89; // a4紙高度
            let pageNum = Math.ceil(totalHeight / a4PageHeight);  // 總頁(yè)數(shù)
            let addPageHeight = 0;  // 修正被分割元素而增加的頁(yè)面高度總和
            let currentPage = 1;  // 當(dāng)前正在處理切割的頁(yè)面
            const splitItemObj: { [key: number]: HTMLElement[] } = {};  // 內(nèi)容中各頁(yè)被切割元素存儲(chǔ)對(duì)象
          
            const printItemNodes: NodeListOf<HTMLElement> = printContainerRef.querySelectorAll(`.${itemClassName}`);
            for (let item of printItemNodes) {
              // 如果當(dāng)前頁(yè)已經(jīng)是最后一頁(yè), 則中斷判斷
              if (currentPage >= pageNum) {
                break;
              }
          
              // 獲取元素絕對(duì)定位數(shù)據(jù)
              const clientRect = item.getBoundingClientRect();
              let top = clientRect.top;
              const selfHeight = clientRect.height;
              // 如果當(dāng)前元素距離頂部高度大于當(dāng)前頁(yè)面頁(yè)腳高度, 則開始判斷下一頁(yè)頁(yè)腳被切割元素
              if (top > currentPage * a4PageHeight) {
                // 換頁(yè)前修正上一頁(yè)被切割元素
                addPageHeight += fixSplitItems(currentPage, a4PageHeight, splitItemObj[currentPage], outFlowClassName);
                pageNum = Math.ceil((totalHeight + addPageHeight) / a4PageHeight);
                top = item.getBoundingClientRect().top;
                currentPage++;
              }
              // 如果元素剛好處于兩頁(yè)之間, 則記錄該元素
              if (top > (currentPage - 1) * a4PageHeight && top < currentPage * a4PageHeight && top + selfHeight > currentPage * a4PageHeight) {
                if (!splitItemObj[currentPage]) {
                  splitItemObj[currentPage] = [];
                }
                splitItemObj[currentPage].unshift(item);
                // 如果當(dāng)前元素是最后一個(gè)元素, 則直接處理切割元素, 否則交由處理下一頁(yè)元素時(shí)再處理切割
                if (item === printItemNodes[printItemNodes.length - 1]) {
                  fixSplitItems(currentPage, a4PageHeight, splitItemObj[currentPage], outFlowClassName);
                }
              }
            }
          }
          
          /**
            * 修復(fù)當(dāng)前頁(yè)所有被切割元素
            * @param currentPage 當(dāng)前頁(yè)
            * @param pageHeight 每頁(yè)高度
            * @param splitElementItems 當(dāng)前被切割元素?cái)?shù)組
            * @param outFlowClassName 脫離文檔流的樣式類名
            */
          function fixSplitItems(
            currentPage: number,
            pageHeight: number,
            splitElementItems: HTMLElement[],
            outFlowClassName: string
          ): number {
            if (!splitElementItems || !splitElementItems.length) {
              return 0;
            }
          
            const yMargin = 5;  // y方向距離頁(yè)眉的距離
            const splitItemsMinTop = getSplitItemsMinTop(splitElementItems);
            if (!splitItemsMinTop) {
              return 0;
            }
          
            let fixHeight = currentPage * pageHeight - splitItemsMinTop + yMargin;
            const outFlowElement = splitElementItems.find((item) => item.classList.contains(outFlowClassName));
            if (outFlowElement && outFlowElement.parentElement) {
              const parentPreviousElement = outFlowElement.parentElement.previousElementSibling as HTMLElement;
              fixHeight += getMarinTopNum(parentPreviousElement, outFlowElement.parentElement);
              outFlowElement.parentElement.style.marginTop = `${fixHeight}px`;
              return fixHeight;
            }
          
            splitElementItems.forEach((splitElement) => {
              splitElement.style.marginTop = `${fixHeight}px`;
            });
            return fixHeight;
          }
          
          /**
            * 獲取被切割元素?cái)?shù)組中最小高度值(如一行有多個(gè)元素被切割,則選出距離頂部最小的高度值)
            * @param splitElementItems 當(dāng)前被切割元素?cái)?shù)組
            */
          function getSplitItemsMinTop(
            splitElementItems: HTMLElement[]
          ): number | undefined {
            // 獲取元素中最小top值作為基準(zhǔn)進(jìn)行修正
            let minTop: number | undefined;
            let minElement: HTMLElement | undefined;
            splitElementItems.forEach((splitElement) => {
              let top = splitElement.getBoundingClientRect().top;
              if (minTop) {
                minTop = top < minTop ? top : minTop;
                minElement = top < minTop ? splitElement : minElement;
              } else {
                minTop = top;
                minElement = splitElement;
              }
            });
          
            // 修正當(dāng)前節(jié)點(diǎn)及其前面同層級(jí)節(jié)點(diǎn)的margin值
            if (minTop && minElement) {
              const previousElement = splitElementItems[splitElementItems.length - 1].previousElementSibling as HTMLElement;
              minTop -= getMarinTopNum(previousElement, minElement);
            }
            return minTop;
          }
          
          /**
            * 通過(guò)前一個(gè)兄弟元素和元素自身的位置確認(rèn)一個(gè)距離頂部高度修正值
            * @param previousElement 前一個(gè)兄弟元素
            * @param curElement 當(dāng)前元素
            */
          function getMarinTopNum(previousElement: HTMLElement, curElement: HTMLElement): number {
            let preMarginNum = 0;
            let curMarginNum = 0;
            if (previousElement) {
              // 獲取外聯(lián)樣式需要getComputedStyle(), 直接.style時(shí)對(duì)象的值都為空
              const previousMarginBottom = window.getComputedStyle(previousElement).marginBottom;
              preMarginNum = previousMarginBottom ? Number(previousMarginBottom.replace('px', '')) : 0;
            }
            const marginTop = window.getComputedStyle(curElement).marginTop;
            curMarginNum = marginTop ? Number(marginTop.replace('px', '')) : 0;
            return preMarginNum > curMarginNum ? preMarginNum : curMarginNum;
          }

          以上純?cè)瓌?chuàng)!歡迎加關(guān)注、加收藏、點(diǎn)贊、轉(zhuǎn)發(fā)、分享(代碼閑聊站)~

          0230112星期四:

          當(dāng)今互聯(lián)網(wǎng)時(shí)代,HTML 和 Markdown 都是非常流行的標(biāo)記語(yǔ)言。HTML 用于構(gòu)建網(wǎng)頁(yè)結(jié)構(gòu)和內(nèi)容,而 Markdown 則以其簡(jiǎn)潔易讀的語(yǔ)法被廣泛用于編寫文檔、博客文章等。在很多場(chǎng)景下,我們需要將 HTML 內(nèi)容轉(zhuǎn)換為 Markdown 格式,例如網(wǎng)頁(yè)內(nèi)容抓取、文檔格式轉(zhuǎn)換等。

          Rust 作為一門以安全、性能和可靠性著稱的系統(tǒng)級(jí)編程語(yǔ)言,也提供了相應(yīng)的庫(kù)來(lái)處理 HTML 到 Markdown 的轉(zhuǎn)換。本文將介紹一個(gè)名為 htmd 的 Rust 庫(kù),它提供了一種高效且易于使用的方式來(lái)完成這項(xiàng)任務(wù)。

          htmd 簡(jiǎn)介

          htmd 是一個(gè)受 JavaScript 庫(kù) turndown.js 啟發(fā)而開發(fā)的 Rust 庫(kù),用于將 HTML 文檔轉(zhuǎn)換為 Markdown 格式。它具有以下特點(diǎn):

          • 功能豐富: 提供了與 turndown.js 相媲美的豐富選項(xiàng),可以靈活地控制轉(zhuǎn)換過(guò)程。
          • 可靠性高: 通過(guò)了 turndown.js 的所有測(cè)試用例,確保轉(zhuǎn)換結(jié)果的準(zhǔn)確性。
          • 依賴項(xiàng)少: 僅依賴于 html5ever 庫(kù),輕量級(jí)且易于集成。
          • 性能優(yōu)異: 轉(zhuǎn)換速度快,能夠高效處理大型 HTML 文檔。

          htmd 的安裝

          在使用 htmd 之前,我們需要先安裝它。將以下依賴項(xiàng)添加到你的 Cargo.toml 文件中:

          [dependencies]
          htmd = "0.1"

          htmd 的使用

          基本轉(zhuǎn)換

          使用 htmd 進(jìn)行 HTML 到 Markdown 的基本轉(zhuǎn)換非常簡(jiǎn)單。以下代碼展示了如何將一個(gè) <h1> 標(biāo)簽轉(zhuǎn)換為 Markdown 中的 # 標(biāo)題:

          use htmd::HtmlToMarkdown;
          
          fn main() {
              let markdown = HtmlToMarkdown::new();
              let html = "<h1>這是一個(gè)標(biāo)題</h1>";
              let result = markdown.convert(html).unwrap();
              assert_eq!(result, "# 這是一個(gè)標(biāo)題\n");
          }

          跳過(guò)標(biāo)簽

          在某些情況下,我們可能希望跳過(guò) HTML 文檔中的某些標(biāo)簽,例如 <script><style> 標(biāo)簽。htmd 提供了 skip_tags 方法來(lái)實(shí)現(xiàn)這一點(diǎn):

          use htmd::HtmlToMarkdown;
          
          fn main() {
              let markdown = HtmlToMarkdown::builder()
                  .skip_tags(vec!["script", "style"])
                  .build();
          
              let html = r#"
                  <script>
                      // 一些 JavaScript 代碼
                  </script>
                  <h1>這是一個(gè)標(biāo)題</h1>
              "#;
          
              let result = markdown.convert(html).unwrap();
              assert_eq!(result, "# 這是一個(gè)標(biāo)題\n");
          }

          自定義標(biāo)簽處理器

          htmd 允許我們自定義標(biāo)簽處理器來(lái)處理特定的 HTML 標(biāo)簽。例如,我們可以定義一個(gè)處理器將 <img> 標(biāo)簽轉(zhuǎn)換為 Markdown 中的圖片語(yǔ)法:

          use htmd::{HtmlToMarkdown, HandleResult};
          use html5ever::tendril::StrTendril;
          use html5ever::QualName;
          
          fn main() {
              let markdown = HtmlToMarkdown::builder()
                  .add_handler(
                      vec!["img"],
                      |el| {
                          let src = el
                              .attrs
                              .iter()
                              .find(|(name, _)| name.local.as_ref() == "src")
                              .map(|(_, value)| value.as_ref());
          
                          let alt = el
                              .attrs
                              .iter()
                              .find(|(name, _)| name.local.as_ref() == "alt")
                              .map(|(_, value)| value.as_ref());
          
                          match (src, alt) {
                              (Some(src), Some(alt)) => Some(format!("![{}]({})", alt, src)),
                              _ => None,
                          }
                      },
                  )
                  .build();
          
              let html = r#"<img src="https://example.com/image.jpg" alt="圖片描述">"#;
              let result = markdown.convert(html).unwrap();
              assert_eq!(result, "![圖片描述](https://example.com/image.jpg)\n");
          }

          多線程處理

          htmd 支持多線程處理,可以充分利用多核 CPU 的性能來(lái)加速轉(zhuǎn)換過(guò)程。以下代碼展示了如何使用多線程將多個(gè) HTML 文檔轉(zhuǎn)換為 Markdown:

          use htmd::HtmlToMarkdown;
          use std::sync::Arc;
          use std::thread;
          
          fn main() {
              let markdown = Arc::new(HtmlToMarkdown::new());
              let html_docs = vec![
                  "<h1>文檔 1</h1>",
                  "<h2>文檔 2</h2>",
                  "<h3>文檔 3</h3>",
              ];
          
              let threads: Vec<_> = html_docs
                  .into_iter()
                  .map(|html| {
                      let markdown = markdown.clone();
                      thread::spawn(move || {
                          let result = markdown.convert(html).unwrap();
                          println!("{}", result);
                      })
                  })
                  .collect();
          
              for thread in threads {
                  thread.join().unwrap();
              }
          }

          結(jié)論

          htmd 是一個(gè)功能強(qiáng)大且易于使用的 Rust 庫(kù),用于將 HTML 文檔轉(zhuǎn)換為 Markdown 格式。它提供了豐富的選項(xiàng)、自定義標(biāo)簽處理器和多線程支持,可以滿足各種轉(zhuǎn)換需求。如果你正在尋找一種高效可靠的 HTML 到 Markdown 轉(zhuǎn)換解決方案,htmd 絕對(duì)值得一試。


          主站蜘蛛池模板: 无码免费一区二区三区免费播放 | 激情一区二区三区| 在线精品国产一区二区三区 | 精品一区二区三区影院在线午夜| 国产区精品一区二区不卡中文| 国产视频一区在线播放| 国产在线精品一区二区中文| 波多野结衣中文字幕一区| 亚洲国产欧美日韩精品一区二区三区| a级午夜毛片免费一区二区| 午夜精品一区二区三区在线观看| 久久精品日韩一区国产二区| 91视频一区二区三区| 亚洲午夜在线一区| 精品无码AV一区二区三区不卡| 亚洲综合av永久无码精品一区二区 | 亚洲AV无码一区二区三区牛牛| 亚欧色一区W666天堂| 国产精品熟女一区二区| 久久99国产精一区二区三区| 真实国产乱子伦精品一区二区三区| 国产精品一区二区三区99| 国产精久久一区二区三区| 国产一区二区三区在线影院| 丝袜人妻一区二区三区网站| 一区二区三区影院| 国产熟女一区二区三区四区五区| 久久成人国产精品一区二区| 国产在线精品一区二区不卡麻豆| 国产A∨国片精品一区二区| 中文字幕aⅴ人妻一区二区| 性色AV一区二区三区| 亚洲国产视频一区| 精品无码综合一区二区三区| 午夜在线视频一区二区三区| 视频一区视频二区日韩专区| 亚洲国产成人精品久久久国产成人一区二区三区综| 丰满岳乱妇一区二区三区| 日韩一本之道一区中文字幕| 性色AV一区二区三区| 国产成人精品一区二区A片带套|