บทความเทคนิค

Figure ของ tagged PDF จากภาพ Excel ด้วย HotXLS

เมื่อ HotXLS export เวิร์กชีตเป็น PDF โดยเปิดการแท็กอัตโนมัติ ภาพเวิร์กชีตที่แบกข้อความทางเลือกจะถูกปล่อยออกมาเป็น structure element แบบ /Figure อิสระ พร้อม entry /Alt แบบ Unicode, marked-content identifier แบบหนาแน่นจำกัดอยู่ระดับหน้า และ entry ของ parent tree ที่ตรงเป๊ะ ภาพที่ไม่มีข้อความทางเลือกยังเป็น artifact เชิงตกแต่ง และ chart ก็ยังเป็น artifact เช่นเดียวกัน ขอบเขตที่เจาะจงแบบนี้สำคัญ: มันทำให้ภาพที่มีข้อมูลเข้าถึงได้ด้วย screen reader และมันไม่ใช่สิ่งเดียวกับความเป็น PDF/UA conformance ฉบับเต็ม

กลไกเบื้องหลังน่าสนใจกว่าคำอธิบายฟีเจอร์เสียอีก เพราะสองข้อในนั้นเป็นรายละเอียดชนิดที่ผลิต PDF ที่ถูกต้องเชิงโครงสร้างอย่างเงียบ ๆ ทั้งที่โครงสร้างชี้ไปที่เนื้อหาที่ผิด

อะไรบ้างที่นับเป็นภาพที่มีข้อมูล

มีเพียง AltText ที่ไม่ว่างเปล่าเท่านั้น property TXLSXImage.AltText วนกลับมาจาก attribute descr ของ OOXML ใน non-visual properties ของภาพ ซึ่งเป็นที่ที่ Excel เก็บข้อความที่ผู้ใช้พิมพ์ลงช่อง alt-text นั่นคือสัญญาณเดียวในไฟล์ที่บอกว่าผู้เขียนมองว่าภาพนี้แบกข้อมูล ไม่ใช่การตกแต่ง จึงเป็นสัญญาณเดียวที่ตัว exporter เชื่อ

มีสองของที่ใกล้เคียงแต่ถูกปฏิเสธโดยตั้งใจ field title ที่ถูกเก็บแยกจากคำอธิบายไม่ใช่สิ่งทดแทน: title คือชื่อของออบเจกต์ ไม่ใช่คู่ข้อความเทียบเท่าของมัน การเลื่อนมันขึ้นไปเป็น /Alt จะได้เอกสารที่ผ่านการเช็กอัตโนมัติ ทั้งที่ประกาศว่า "Picture 3" ให้ screen reader คำอธิบายว่างเปล่าก็ไม่ใช่ช่องว่างที่ต้องเติมข้อความแทน มันหมายความว่าภาพยังเป็น artifact ซึ่งเป็นผลที่ถูกต้องสำหรับ logo หรือเส้นคั่น chart ยังเป็น artifact อยู่ในขณะนี้ เพราะคู่ข้อความเทียบเท่าของ chart คือข้อมูลของมัน และการสังเคราะห์ขึ้นจาก series คือการแต่งขึ้น ไม่ใช่การดึงออกมา

ภาพที่ AltText ไม่ว่างถูก export เป็น structure element แบบ Figure ของ PDF พร้อม MCID ของตัวเอง คำอธิบายว่างเปล่าและ chart ยังเป็น artifact
มีเพียงคำอธิบายของผู้เขียนใน AltText เท่านั้นที่เป็นสัญญาณของภาพที่มีข้อมูล title อย่างเดียวไม่มีวันกลายเป็น alt text
uses
  lxHandleX, lxPDF;

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Exporter: TXLSPDFExport;
  I: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('regional-review.xlsx');
    Sheet := Book.Sheets.ByPos[0];

    // ตรวจก่อน export: ภาพที่ไม่มีคำอธิบาย
    // จะถูก export เป็น artifact เชิงตกแต่ง
    for I := 0 to Sheet.Images.Count - 1 do
      if Sheet.Images[I].AltText = '' then
        Sheet.Images[I].AltText := DescribeImage(Sheet.Images[I].Name);

    Exporter := TXLSPDFExport.Create;
    try
      Exporter.TagMode := xlsPdfTagsAutomatic;
      Exporter.DocumentLanguage := 'en-US';
      Exporter.SaveAsPDF(Sheet, 'regional-review.pdf');
    finally
      Exporter.Free;
    end;
  finally
    Book.Free;
  end;
end;

ทำไมหน้าหนึ่งหน้าจึงต้องมีตัวแบ่ง MCID ตัวเดียว

เพราะ parent tree เป็นอาร์เรย์ที่ใช้ marked-content identifier เป็นดัชนี และตัวแบ่งสองตัวจะผลิตสอง entry ที่อ้างสลอตเดียวกัน tagged PDF เชื่อมเนื้อหาเข้ากับโครงสร้างทั้งสองทิศทาง ฝั่งเนื้อหา ช่วงหนึ่งของ content stream ถูกห่อด้วย operator BDC และ EMC ที่แบกเลข /MCID ซึ่งไม่ซ้ำภายในหน้านั้น ฝั่งโครงสร้าง dictionary ของหน้าแบก key /StructParents ระบุแถวหนึ่งของ /ParentTree ของเอกสาร และแถวนั้นเป็นอาร์เรย์ที่ element ที่ดัชนี n คือ structure element ที่เป็นเจ้าของ MCID n

หน้าเวิร์กชีตมีเซลล์ตาราง และตอนนี้มี figure ด้วย ถ้าตัวแท็กเซลล์นับ identifier จากศูนย์ และตัวแท็ก figure ก็นับจากศูนย์เช่นกัน figure ตัวแรกจะยึดสลอตที่เซลล์ตัวแรกเป็นเจ้าของแล้ว ไฟล์ที่ได้ไม่มีอะไรเสียมากพอให้ parser ปฏิเสธ: ต้นไม้โครงสร้างสมบูรณ์ marked content สมดุล และ validator เห็นเอกสารที่มี parent tree สิ่งที่ screen reader ได้รับคือเซลล์ตารางที่ถูกประกาศเป็นภาพ หรือภาพที่ถูกประกาศด้วยข้อความของเซลล์ ตัว exporter จึงแบ่งจากตัวนับระดับหน้าตัวเดียวที่ตัวแท็กทั้งสองใช้ร่วมกัน และแช่แข็ง record ของหน้าเมื่อเลขออบเจกต์ของหน้าเป็นที่รู้จักแล้วเท่านั้น เพราะแถว parent tree เขียนไม่ได้ก่อนหน้าที่มันอ้างถึงจะมีตัวตน

ตัวแท็กเซลล์กับตัวแท็ก figure ที่ทำงานอิสระชนกันที่สลอตศูนย์ของ parent tree ตัวนับ MCID ระดับหน้าตัวเดียวทำให้ทุกเครื่องหมายมีเจ้าของเดียว
ไฟล์ที่ชนกันยังผ่าน validator เชิงโครงสร้าง สิ่งที่ผิดมีเพียงการประกาศของ screen reader

Figure ต้องครอบ instance ที่มองเห็นทั้งหมด

การวางแบบไร้เดียงสาคือห่อ operator Do ที่เรียกใช้ image XObject เพราะนั่นคือ operator ที่วาดภาพ แต่มันไม่พอ ภาพเวิร์กชีตมักถูกวาดพร้อมเงาด้านหลังและ clipping path ล้อมรอบ และเครื่องหมายเหล่านั้นเป็นส่วนของออบเจกต์ที่มองเห็น ถ้าทิ้งอยู่นอก scope ของ /Figure พวกมันจะกลายเป็นเนื้อหาที่ไม่มีเครื่องหมาย ซึ่งเป็นสถานะพอดีที่การ audit โครงสร้างตั้งใส่

scope ของ marked content จึงเปิดก่อนเงาและปิดหลังการวาดภาพ ครอบ clip ไปด้วย การแชร์ถูกรักษาไว้ที่ที่การแชร์ถูกต้อง: สองเซลล์ที่แสดง payload ภาพเดียวกันยังอ้างถึง image XObject ตัวเดียว เพราะนั่นเป็นการปรับแต่งระดับ resource และไม่เกี่ยวกับ semantics สิ่งที่ instance ที่มองเห็นแต่ละตัวได้รับคือ MCID และ structure element ของตัวเอง เพราะ logo เดียวกันสองที่คือสองสิ่งที่ผู้อ่านเจอ การวางภาพและเรขาคณิต EMU ที่กำหนดตำแหน่งออบเจกต์เหล่านี้อธิบายไว้ในบทความเรขาคณิตของภาพ

เครื่องหมาย BDC เปิด scope ของ Figure ก่อนเงาและ clip และ EMC ปิดหลังการวาดภาพด้วย Do ครอบ instance ที่มองเห็นทั้งหมด
ห่อเฉพาะ operator ภาพแล้วเงากับ clip จะหลุดเป็นเนื้อหาไร้เครื่องหมาย การแชร์ resource ระหว่างเซลล์ยังถูกรักษาไว้

ลำดับการอ่านบนหน้าเวิร์กชีต

ลำดับการอ่านเป็นการตัดสินใจที่ตัว exporter ต้องทำ เพราะ spreadsheet ไม่มี flow ที่ถูกแต่งไว้แบบเอกสาร กฎที่ยอมรับมาเสถียรและอธิบายง่าย: ต่อหนึ่งหน้า ตารางมาก่อน แล้วจึงเป็น figure ตามลำดับการวาด ผู้อ่านจึงได้ยินเนื้อหาตารางของหน้าแล้วตามด้วยภาพ แทนที่ภาพจะสอดแทรกตามตำแหน่งที่วัตถุวาดภาพบังเอิญนั่งอยู่ในไฟล์

ลำดับนั้นเป็นรายหน้า ไม่ใช่รายเอกสาร ซึ่งสำคัญกับเวิร์กบุ๊กที่แบ่งหน้าเป็นหลายสิบหน้า: สาขาโครงสร้างของแต่ละหน้าสมบูรณ์ในตัวเอง ผู้อ่านที่เคลื่อนข้ามหน้าจึงไม่ถูกดีดกลับไปที่ตารางเก่า ถ้าคุณต้องการคุมว่าชีตแบ่งหน้าอย่างไรในตอนแรก ปฏิสัมพันธ์ระหว่าง page setup และ print area อธิบายไว้ในบทความการป้องกันและ page setup

สิ่งที่สิ่งนี้รับรอง และสิ่งที่มันไม่รับรอง

มันรับรองว่าภาพที่มีข้อมูลไปถึงเทคโนโลยีช่วยเหลือพร้อมคำอธิบายที่ผู้เขียนให้มา และ mapping จากเนื้อหาเข้าสู่โครงสร้างถูกต้อง ไม่ใช่แค่มีอยู่ มันไม่ได้ทำให้ผลลัพธ์เป็น PDF/UA ตามมาตรฐาน และการเรียกมันแบบนั้นคือคำกล่าวอ้างที่ implementation ไม่อาจรองรับ: chart ยังเป็น artifact อยู่ และคำประกาศ conformance ฉบับเต็มต้องการการ audit structure type ทุกชนิด font ทุกตัว และ metadata ของเอกสารทั้งชุด

ถ้าข้อกำหนดของคุณคือ profile เพื่อการจัดเก็บหรือ conformance แทนการยกระดับ accessibility นั่นเป็น configuration การ export อีกแบบหนึ่งและชุดการเช็กอีกชุดหนึ่ง ตามที่อธิบายไว้ในบทความ export เพื่อจัดเก็บรูปแบบ PDF/A สองอย่างนี้ผสมกันได้ แต่พวกมันตอบ auditor คนละราย

ข้อเสนอเชิงปฏิบัติหนึ่งข้อสำหรับ pipeline รายงาน: audit ข้อความทางเลือกที่จุดที่เวิร์กบุ๊กถูกสร้าง ไม่ใช่ตอน export ผู้สร้างรู้ว่าภาพ chart หรือ diagram ที่ฝังมาแต่ละตัวแทนอะไร และเขียนคำอธิบายจริงลง AltText ได้ การเช็กช่วง export บอกคุณได้เพียงว่าคำอธิบายขาดหาย HotXLS อ่านและเขียน XLS, XLSX, ODS และ CSV แบบ native จาก Delphi และ C++Builder โดยไม่พึ่ง Excel และตัวเลือก configuration การ export ระบุไว้บนหน้าผลิตภัณฑ์HotXLS Delphi spreadsheet component